美文网首页
回溯算法

回溯算法

作者: f155b8f6e0ac | 来源:发表于2020-03-16 20:21 被阅读0次

思想

回溯法(back tracking)(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

回溯法解求解问题步骤

  1. 针对给定问题,定义问题的解空间树;(这一步非常重要!!
  2. 确定易于搜索的解空间结构;
  3. 以深度优先方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索;
  4. 用回溯法求解问题,重点是设计问题的解空间树,其解题过程则是深度遍历解空间树的过程。

注:在回溯法中通常会使用到“递归”和“剪枝”

常用的剪枝函数:用约束函数在扩展结点处剪去不满足约束的子树;用限界函数剪去得不到最优解的子树。
回溯法对解空间做深度优先搜索时,有递归回溯和迭代回溯(非递归)两种方法,但一般情况下用递归方法实现回溯法。

模板

回溯法的一般模板,可以如下所示:

demoMethod() {
    //生成回溯结果的存储位置
    Object res = new Object();
    //边界值判定,比如n == 0之类的。
    if() return res;
    //回溯递归函数,先写出来占个坑,参数一会儿加。
    backTrack();
    //返回,程序结束。
    return res;

}

backTrack(...args[]) {   //参数根据题目实际情况来定,数量一般会在4-5个左右
    // 递归的结束条件
    if () {
    }
    //回溯算法的主体,包括前进和回溯,sub是存储结果
    for(){   
    add()  // 1.一个解中的某个元素,进行添加;
   backTrack(index + 1);    // 2.进行深搜,进行到解空间树的下一层
    sub.remove(sub.size() - 1);  // 3.*回溯到解空间树的上一层
    }
    
}

需要注意的是:

  • for循环的次数;:在解空间树种每一层有多少种可能的答案,就要循环多少次。
  • 以及递归的结束条件:当达到解空间树的叶子节点,即递归的结束条件一般会跟解空间树的深度有关。

具体我们以下面的例子做讲解:

例子

Leetcode上有很多回溯算法问题。这里用一个leetcode上比较经典的题目来做示例
传送门:https://leetcode-cn.com/problems/permutations-ii/

全排列.png

下面将详细介绍一下解题思路:(此思路适用回溯法80%的题目):
1.定义问题的解空间树,如下图所示;
2.分析发现,在回溯函数中循环的次数应该是每次剩下数组的长度(因为每次都会存储一个值);如下图中横框框住的元素个数,第一层3种情况;第二层2种情况;第三层1种情况;
3.递归结束条件:当当前的解中的元素经满了(即3个,给定的数组的长度),则跳出本层,回到上层树。

值得注意的是:本题有个要求是,答案不能有重复的。如果不进行剪枝,则会有6个答案,但是如图所示有两处可以剪枝,那剪枝的条件是什么呢?我们发现,只要在每层访问的时候,如果这个节点我们已经访问过了,我们就可以直接跳过,例如:在第一层,第二个的1已经访问过,所以我们可以跳过。因此我们可以在每一层都维护一个已经访问的节点即可。

image.png

解答

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> deque = new LinkedList<>();
        if (nums.length == 0) {
            return res;
        }

        for (int i = 0; i < nums.length; i++) {
            deque.add(nums[i]);
        }

        backTrack(res, new ArrayList<>(), deque, nums.length);
        return res;
    }

    /**
     * 
     * @param res - 存储最后的结果,即返回值
     * @param curList - 存储当前的值
     * @param deque - 解空间树种每一层可能的情况的集合
     * @param length - 递归结束条件的长度,即树的深度
     */
    private static void backTrack(List<List<Integer>> res, List<Integer> curList, List<Integer> deque, int length) {
        if (curList.size() == length) {
            res.add(new ArrayList<>(curList));
        }
        int size = deque.size();
        List<Integer> visited = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            // 剪枝函数
            if (visited.contains(deque.get(i))) {
                continue;
            }
            visited.add(deque.get(i));
            List<Integer> temp = new ArrayList<>(deque);
            curList.add(deque.get(i));
            temp.remove(i);
            backTrack(res, curList, temp, length);
            curList.remove(curList.size() - 1);
        }
    }
}

相关文章

  • 回溯算法

    回溯算法 回溯算法介绍   回溯算法并不是非常复杂的算法, 很多人接触过但是未必知道这是回溯算法; 典型的回溯算法...

  • 回溯算法:八皇后问题和0-1背包问题

    文章结构 如何理解回溯算法 回溯算法的经典应用 完整源码 图片来源 1. 如何理解回溯算法 1.1 什么是回溯算法...

  • Algorithm进阶计划 -- 回溯算法

    滑动窗口算法回溯算法框架回溯算法运用 1. 回溯算法框架 回溯算法,是类似枚举的搜索尝试过程,主要是在搜索尝试过程...

  • 回溯算法总结

    回溯法学习总结 回溯算法也是算法导论中常用的算法,回溯算法类似于暴力求解算法,经常用在求可能解的问题。下面我将从三...

  • 77. Combinations.go

    回溯算法

  • 递归2-回溯与递归

    I. 回溯算法基础 回溯与递归的区别和联系  很多人不理解回溯与递归到底是什么关系。其实很简单,回溯算法是一种算法...

  • 回溯算法之-组合总和

    回溯算法模版 首先上一套回溯算法模版,很多回溯算法都可以使用该模版解决 leetcode 39 组合总和 给定一个...

  • 17. Letter Combinations of a Pho

    使用回溯算法

  • 「回溯算法」专题介绍

    「回溯算法」专题介绍 第 1 节:从全排列问题开始理解回溯搜索算法 引言 大家好,今天要和大家分享的主题是“回溯算...

  • 450,什么叫回溯算法,一看就会,一写就废

    什么叫回溯算法 对于回溯算法的定义,百度百科上是这样描述的:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索...

网友评论

      本文标题:回溯算法

      本文链接:https://www.haomeiwen.com/subject/pcmoehtx.html