Hot100-回溯

 回溯:

有递归就会有回溯,递归和回溯相辅相成,回溯通常藏在递归函数的下面。

回溯函数其实就是递归函数,没有单独的回溯函数

效率:

回溯函数是一个纯暴力的搜索。

有的问题用for循环一点一点搜索都搜索不出来,一定要用回溯才能搜索出来

哪些问题for循环一点一点搜索都搜索不出来,一定要用回溯才能搜索出来?

        组合问题。

                e.g. 1234有多少种两两组合?  12 13 14.。。。

        切割问题。

                e.g. 给一个字符串,有几种切割方式?

        子集问题。

                e.g. 1234的子集有哪些?1 2 3 4 12 13 14。。。

        排列问题。

                e.g. 12的排列有12和21.

        棋盘问题。

                e.g. N皇后、解数独

怎么理解回溯法?

把回溯法抽象为图像,做题的时候要画图。

回溯法可以抽象为N叉树,树的宽度是要处理的集合的大小,深度是递归的深度(要通过递归处理)

回溯的模板

在回溯算法中,我的习惯是函数起名字为backtracking,这个起名大家随意。

回溯算法中函数返回值一般为void。

void backtracking(参数){
//什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。
if (终止条件) {
    存放结果;
    return;
}
//回溯函数遍历过程伪代码如下
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
    处理节点;
    backtracking(路径,选择列表); // 递归,根据树,从上往下递归
    回溯,撤销处理结果
}
}
//为什么要撤销处理结果?
//比如123排列组合问题
//12处理完以后,要把12撤销,组合13;13处理完以后,要把13撤销,组合23

回溯三部曲

递归函数参数返回值

确定终止条件

单层递归逻辑

回溯的题:

39. 组合总和 - 力扣(LeetCode)

7.4组合总和(LC39-M)-CSDN博客

class Solution {
    //全局变量
    List<Integer> path = new LinkedList();
    List<List<Integer>> result = new LinkedList();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backtracking(candidates, target, 0, 0);
        return result;
    }

    //回溯
    public void backtracking(int[] candidates, int target, int sum, int startindex){
        //确定终止条件
        if (sum > target){
            return;
        }
        if (sum == target){
            result.add(new LinkedList(path));//每次要复制一个path,不能在原path基础上操作
            return;
        }
        //回溯+递归
        for(int i=startindex; i < candidates.length && sum <= target; i++){
            path.add(candidates[i]);
            sum += candidates[i];
            //单条路径递归
            backtracking(candidates, target, sum, i);
            //回溯,撤销上一步
            sum -= candidates[i];
            path.removeLast();
        }

    }
}

46. 全排列 - 力扣(LeetCode)

class Solution {
    List<Integer> path = new LinkedList();
    List<List<Integer>> result = new LinkedList();
    public List<List<Integer>> permute(int[] nums) {
        boolean[] used2 = new boolean[nums.length];
        backtracking(nums, used2 );
        return result;

    }
    public void backtracking(int[] nums, boolean[] used){
        //确定终止条件
        if (nums.length == path.size()){
             result.add(new LinkedList(path));
            return;}
       
        //递归+回溯
        for(int i = 0; i < nums.length; i++){
            if (used[i] == true){
                continue;
            }
            path.add(nums[i]);
            used[i] = true;//标记已使用
            backtracking(nums, used);
            //回溯,撤销上一步
            //回溯底层是栈,先入后出,先加元素,此时后移除元素
            used[i] = false;
            path.remove(path.size()-1);
        }
        
    }
}

78. 子集 - 力扣(LeetCode)

其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。

那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!

什么时候for可以从0开始呢?

求排列问题的时候,就要从0开始,因为集合是有序的,{1, 2} 和{2, 1}是两个集合。

class Solution {
    List<Integer> path = new LinkedList();
    List<List<Integer>> result = new LinkedList();

    public List<List<Integer>> subsets(int[] nums) {
        backTracking(nums, 0);
        return result;
    }

    //无序,需要startindex
    public void backTracking (int[] nums, int startindex){
        //每种可能的结果都要
        result.add(new LinkedList(path));
        //终止条件
        if (startindex >= nums.length){
            return;
        }
        //回溯+递归
        for(int i=startindex; i<nums.length; i++){
            path.add(nums[i]);
            backTracking(nums, i+1);//注意:这里是i+1,和组合问题不同,这里每次递归就是一个结果,直接调用下一次递归即可
            //回溯
            path.remove(path.size()-1);
        }
    }
}

17. 电话号码的字母组合 - 力扣(LeetCode)

 7.3电话号码的字母组合(LC17-M)-CSDN博客

class Solution {
    List<String> result = new LinkedList();
    StringBuilder path = new StringBuilder();
    public List<String> letterCombinations(String digits) {
        // 处理空输入情况
        if (digits.equals("") || digits == null) return result;
        String[] telephone = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        backtracking(digits, 0, telephone);
        return result;

    }

    //1.确定返回值和参数
    void backtracking (String digits, int index, String[] tele){
        //2.确定终止条件
        if (index == digits.length()){
            result.add(path.toString());
            return;
        }
        //3.单层递归逻辑
        //str表示数字对应的字母,如2-abc
        String str = tele[digits.charAt(index) - '0'];
     

        for (int i = 0; i < str.length(); i++){
            path.append(str.charAt(i));

            backtracking(digits, index+1, tele);

            path.deleteCharAt(path.length()-1);

        }


    }
}

22. 括号生成 - 力扣(LeetCode)

class Solution {
    List<String> result = new LinkedList();
    StringBuilder path = new StringBuilder();
    public List<String> generateParenthesis(int n) {
        //其实就是左括号和右括号的有效组合
        //有序的:左括号一定在右括号前面(通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点)
        //如果左括号数量小于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。
        backtracking(n, 0, 0);
        return result;}

        //回溯
        //1.确定返回值和参数
        //返回值:void
        //参数:括号对数量,n;记录当前递归时左括号的数量:left;记录当前递归时右括号的数量:right
    public void backtracking(int n, int left, int right){
        //2.终止条件
        if (path.length() == 2*n){
            result.add(path.toString());
            return;
        }

        //3.单层递归逻辑:如果左括号数量小于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。
        if (left < n){
            path.append('(');
            backtracking(n, left+1, right);
            path.deleteCharAt(path.length()-1);
        }
        if (right < left){
            path.append(')');
            backtracking(n, left, right+1);
            path.deleteCharAt(path.length()-1);
        }
    }
    }

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值