当前位置:   article > 正文

回溯法、DFS、递归、栈、BFS知识点总结_回溯算法是dfs吗

回溯算法是dfs吗

1. DFS 和回溯算法区别

DFS 是一个劲的往某一个方向搜索,而回溯算法建立在 DFS 基础之上的,但不同的是在搜索过程中,达到结束条件后,恢复状态,回溯上一层,再次搜索。因此回溯算法与 DFS 的区别就是有无状态重置

2. 何时使用回溯算法

当问题需要 "回头",以此来查找出所有的解的时候,使用回溯算法。即满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止

3. 怎么样写回溯算法(从上而下,※代表难点)

①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
②根据题意,确立结束条件
③找准选择列表(与函数参数相关), 与第一步紧密关联※
④判断是否需要剪枝
⑤作出选择,递归调用,进入下一层
⑥撤销选择

4. 回溯问题的类型(三种)

(子集、组合)、全排列、搜索

A、 子集 - 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)

  1. class Solution {
  2. public List<List<Integer>> subsets(int[] nums) {
  3. List<List<Integer>> res = new ArrayList<>();
  4. backtrack(nums, res, 0, new ArrayList<Integer>());
  5. return res;
  6. }
  7. private void backtrack(int[] nums, List<List<Integer>> res, int start, ArrayList<Integer> tmp) {
  8. res.add(new ArrayList<>(tmp));
  9. for(int i = start; i < nums.length; i++) {
  10. tmp.add(nums[i]);
  11. backtrack(nums, res, i + 1, tmp);
  12. tmp.remove(tmp.size() - 1);
  13. }
  14. }
  15. }

B、子集 II(剪枝思想)

 

  1. class Solution {
  2. public List<List<Integer>> subsetsWithDup(int[] nums) {
  3. Arrays.sort(nums);
  4. List<List<Integer>> res = new ArrayList<>();
  5. backtrack(nums, res, 0, new ArrayList<>());
  6. return res;
  7. }
  8. private void backtrack(int[] nums, List<List<Integer>> res, int start, ArrayList<Integer> tmp) {
  9. res.add(new ArrayList<>(tmp));
  10. for(int i = start; i < nums.length; i++) {
  11. if(i > start && nums[i] == nums[i-1]) { //同一层级不出现相同元素;允许不同层级出现相同元素
  12. continue;
  13. }else {
  14. tmp.add(nums[i]);
  15. backtrack(nums, res, i + 1, tmp);
  16. tmp.remove(tmp.size() - 1);
  17. }
  18. }
  19. }
  20. }

C、组合总和 

方法一、回溯法

  1. class Solution {
  2. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  3. Arrays.sort(candidates);
  4. List<List<Integer>> res = new ArrayList<>();
  5. backtrack(candidates, res, 0, 0, target, new ArrayList<>());
  6. return res;
  7. }
  8. private void backtrack(int[] nums, List<List<Integer>> res, int start, int sum, int target, ArrayList<Integer> tmp) {
  9. if(sum == target) { //满足条件
  10. res.add(new ArrayList<>(tmp));
  11. return ;
  12. }
  13. for(int i = start; i < nums.length; i++) {
  14. if(sum > target) { //剪枝
  15. continue;
  16. }
  17. tmp.add(nums[i]);
  18. backtrack(nums, res, i, sum + nums[i], target, tmp);
  19. tmp.remove(tmp.size() - 1);
  20. }
  21. }
  22. }

 方法二:

  1. import java.util.*;
  2. public class Solution {
  3. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  4. int len = candidates.length;
  5. List<List<Integer>> res = new ArrayList<>();
  6. if (len == 0) {
  7. return res;
  8. }
  9. // 排序是剪枝的前提
  10. Arrays.sort(candidates);
  11. //初始化栈
  12. Deque<Integer> path = new ArrayDeque<>();
  13. int begin = 0; //起始位点
  14. dfs(candidates, begin, len, target, path, res);
  15. return res;
  16. }
  17. private void dfs(int[] candidates, int begin, int len, int target, Deque<Integer> path, List<List<Integer>> res) {
  18. // 由于进入更深层的时候,小于 0 的部分被剪枝,因此递归终止条件值只判断等于 0 的情况
  19. if (target == 0) {
  20. res.add(new ArrayList<>(path));
  21. return;
  22. }
  23. for (int i = begin; i < len; i++) {
  24. // 重点理解这里剪枝,前提是候选数组有序,
  25. if (target - candidates[i] < 0) {
  26. break;
  27. }
  28. path.addLast(candidates[i]);
  29. dfs(candidates, i, len, target - candidates[i], path, res);
  30. path.removeLast();
  31. }
  32. }
  33. }

总结:子集、组合类问题,关键是用一个 start 参数来控制选择列表!!

回溯六步:

①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
②根据题意,确立结束条件
③找准选择列表(与函数参数相关),与第一步紧密关联※
④判断是否需要剪枝
⑤作出选择,递归调用,进入下一层
⑥撤销选择

  1. backtrack的公式:
  2. result = []
  3. def backtrack(路径, 选择列表):
  4. if 满足结束条件:
  5. result.add(路径)
  6. return
  7. for 选择 in 选择列表:
  8. 做选择
  9. backtrack(路径, 选择列表)
  10. 撤销选择

组合问题

什么时候使用 used 数组,什么时候使用 begin 变量
排列问题,讲究顺序,需要记录哪些数字已经使用过,此时用 used 数组;
组合问题,不讲究顺序,需要按照某种顺序搜索,此时使用 begin 变量。

40. 组合总和 II

  1. class Solution {
  2. public List<List<Integer>> combinationSum2(int[] candidates, int target) {
  3. int len = candidates.length;
  4. List<List<Integer>> res = new ArrayList<>();
  5. if(len == 0){
  6. return res;
  7. }
  8. Arrays.sort(candidates); // 先排序以使用剪枝
  9. Deque<Integer> path = new ArrayDeque<>();
  10. int begin = 0;
  11. dfs(candidates, begin, len, target, path, res);
  12. return res;
  13. }
  14. private void dfs(int[] candidates, int begin, int len, int target, Deque<Integer> path, List<List<Integer>> res){
  15. //大剪枝,
  16. if(target == 0){
  17. res.add(new ArrayList<>(path));
  18. return;
  19. }
  20. for(int i = begin; i < len; i++){
  21. // 小剪枝:同一层相同数值的结点,从第 2 个开始,候选数更少,结果一定发生重复,因此跳过,用 continue
  22. if (i > begin && candidates[i] == candidates[i - 1]) {
  23. continue;
  24. }
  25. if(target - candidates[i] < 0){
  26. break;
  27. }
  28. path.add(candidates[i]);
  29. dfs(candidates, i + 1, len, target - candidates[i], path, res);
  30. path.removeLast();
  31. }
  32. }
  33. }

46. 全排列

47. 全排列 II


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/478081
推荐阅读
相关标签
  

闽ICP备14008679号