回溯算法--总结与例题整理

本文从以下几个方面对回溯算法进行总结:

1、什么是回溯算法

2、回溯算法与穷举法的区别与联系

3、回溯算法的解题步骤(准备工作、解题步骤、递归方法的参数选择)

4、例题:LeetCode-39 组合总和、LeetCode-40 组合总和Ⅱ

5、例题整理

一、什么是回溯算法

        回溯算法(百度百科)实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。

二、回溯算法与穷举法的区别与联系

        回溯法与穷举法有某些联系,它们都是基于试探的,但是回溯算法是优于穷举法的。穷举法要将一个解的各个部分全部生成后,才检查是否满足条件,若不满足,则直接放弃该完整解,然后再尝试另一个可能的完整解,它并没有沿着一个可能的完整解的各个部分逐步回退生成解的过程。而对于回溯法,一个解的各个部分是逐步生成的,当发现当前生成的某部分不满足约束条件时,就放弃该步所做的工作,退到上一步进行新的尝试,而不是放弃整个解重来。(所谓剪枝,也就是避免了一些不必要的搜索)

三、回溯算法的解题步骤

准备工作:

1)由于采用回溯法求解时存在退回到祖先结点的过程,所以需要保存搜索过的结点。

      通常有两种方法:1、用子定义栈来保存;2、采用递归方法。

2)用回溯法通常采用两种策略避免无效搜索。这两类函数统称为剪枝函数

      1、用约束函数在扩展结点处剪除不满足约束条件的路径;

      2、用限界函数剪去得不到问题的解或最优解的路径。

3)要保证递归函数返回 或 弹出栈顶元素 后,状态可以恢复到操作前,以此达到真正的回溯。

 

回溯法求解的一般步骤:

1、针对给定的问题确定问题的解空间树

2、确定结点的扩展搜索规则。

3、以深度优先方式搜索解空间树,并在搜索过程中可以采用剪枝函数来避免无效搜索。其中,深度优先方式可以采用递归回溯或者非递归(迭代)回溯。


递归方法的参数选择:

1、一个临时变量(可以就直接传递一个字面量或者常量进去)传递不完整的解,因为每一步选择后,暂时还没构成完整的解,这个时候这个选择的不完整解,也要想办法传递给递归函数。也就是,把每次递归的不同情况传递给递归调用的函数。
2、一个全局变量,用来存储完整的每个解,一般是个集合容器(也不一定要有这样一个变量,因为每次符合结束条件,不完整解就是完整解了,直接打印即可)。
3、最重要的一点,一定要在参数设计中,可以得到结束条件。一个选择是可以传递一个量n,也许是数组的长度,也许是数量,等等。

四、例题

LeetCode-39 组合总和 题目链接:https://leetcode-cn.com/problems/combination-sum/

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。 

示例 1:

输入: candidates = [2,3,6,7],target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

示例 2:

输入: candidates = [2,3,5],target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(candidates);
        combinationSum(candidates, target, 0, new ArrayList<>(), result);
        return result;
    }

    // 回溯算法
    // 1、题目为无重复数组,故不需去重
    // 2、数字可被无限重复选取,故从start本身开始递归
    public static void combinationSum(int[] candidates, int target, int start, List<Integer> temp, List<List<Integer>> result) {
        if (target == 0) {
            result.add(new ArrayList<>(temp));
            return;
        }
        if (target < 0) {
            return;
        }
        for (int i = start; i < candidates.length && candidates[i] <= target; i++) {
            temp.add(candidates[i]);
            combinationSum(candidates, target - candidates[i], i, temp, result);
            temp.remove(temp.size()-1);
        }
    }

LeetCode-40 组合总和Ⅱ 题目链接:https://leetcode-cn.com/problems/combination-sum-ii/

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次

说明:

  • 所有数字(包括目标数)都是正整数。
  • 解集不能包含重复的组合。 

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
  [1,2,2],
  [5]
]
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(candidates);
        combinationSum2(candidates, target, 0, new ArrayList<>(), result);
        return result;
    }

    // 回溯算法
    // 1、题目为重复数组,故需要去重
    // 2、数字只能选取一次,故从start+1开始递归
    public static void combinationSum2(int[] candidates, int target, int start, List<Integer> temp, List<List<Integer>> result) {
        if (target < 0) {
            return;
        }
        if (target == 0) {
            result.add(new ArrayList<>(temp));
            return;
        }
        for (int i = start; i < candidates.length && candidates[i] <= target; i++) {
            if (i > start && candidates[i] == candidates[i-1]) // 去重
                continue;
            temp.add(candidates[i]);
            combinationSum2(candidates, target - candidates[i], i+1, temp, result);
            temp.remove(temp.size()-1);
        }
    }

五、例题整理

1、LeetCode-39 组合总和 题目链接:https://leetcode-cn.com/problems/combination-sum/

2、LeetCode-40 组合总和Ⅱ 题目链接:https://leetcode-cn.com/problems/combination-sum-ii/

3、LeetCode-46 全排列 题目链接:https://leetcode-cn.com/problems/permutations/

4、LeetCode-47 全排列Ⅱ 题目链接:https://leetcode-cn.com/problems/permutations-ii/

5、LeetCode-78 子集 题目链接:https://leetcode-cn.com/problems/subsets/

6、LeetCode-90 子集Ⅱ 题目链接:https://leetcode-cn.com/problems/subsets-ii/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章