秋招-算法-回溯算法篇

秋招-算法-回溯算法篇

介绍

​ 回溯实际上是一种试探算法,这种算法跟暴力搜索最大的不同在于,在回溯算法里,是一步一步地小心翼翼地进行向前试探,会对每一步探测到的情况进行评估,如果当前的情况已经无法满足要求,那么就没有必要继续进行下去,也就是说,它可以帮助我们避免走很多的弯路。
​ 回溯算法的特点在于,当出现非法的情况时,算法可以回退到之前的情景,可以是返回一步,有时候甚至可以返回多步,然后再去尝试别的路径和办法。这也就意味着,想要采用回溯算法,就必须保证,每次都有多种尝试的可能。

模板

下面的去重、做剪枝弄不出来,可以直接用set或者List的contain等方法在返回结果前处理下,效率会下降,但至少能对点

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        trim,去重、做剪枝
        choose,选择合适的结果
        backtrack(路径, 选择列表)
        back,撤销选择

常见类型为全排列和组合两种:

在【for 选择 in 选择列表】这步骤上为主要区别,全排列一般是从头开始遍历,而组合一边是接着上次的接口开始遍历

例题

47. 全排列 II

image-20220731231756894

class Solution {

    List<List<Integer>> res;
    public List<List<Integer>> permuteUnique(int[] nums){
        res = new LinkedList<>();
        Arrays.sort(nums);
        boolean[] used = new boolean[nums.length];
        backtrace(nums,new LinkedList<>(),used);
        return res;
    }

    private void backtrace(int[] candidates,LinkedList<Integer> chain,boolean[] used) {
        //base
        if (chain.size()==candidates.length){
            res.add(new LinkedList<>(chain));
            return;
        }else if (chain.size()>candidates.length){
            return;
        }

        //for
        for (int i = 0; i < candidates.length; i++) {
            //trim
            //112 ==> 112 211 211
            if (used[i])    continue;
            if (i>0&&candidates[i]==candidates[i-1]&&!used[i-1])    continue;
            //choose
            chain.add(candidates[i]);
            used[i]=true;
            backtrace(candidates,chain,used);
            //back
            chain.removeLast();
            used[i]=false;
        }
    }
}

40. 组合总和 II

image-20220731212850165

class Solution {

    List<List<Integer>> res;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        res = new LinkedList<>();
        Arrays.sort(candidates);
        backtrace(target,candidates,0,new LinkedList<>(),0);
        return res;
    }

    private void backtrace(int target, int[] candidates,int sum,LinkedList<Integer> chain,int pos) {
        //base
        if (sum==target){
            res.add(new LinkedList<>(chain));
            return;
        }else if (sum>target){
            return;
        }

        //for
        for (int i = pos; i < candidates.length; i++) {
            //trim
            //加入给了 1,1,6,7 target=8 , ==> 可以选择: 116 ,有两个1要对两个1,7去重
            //116 是通过回溯递归获得的第二个1
            //17  17,是通过for循环获得的第二个1
            //循环会造成i>pos,回溯递归i=pos,借此进行剪枝、去重
            if (i>pos&&candidates[i]==candidates[i-1]){
                continue;
            }
			//choose
            chain.add(candidates[i]);
            backtrace(target,candidates,sum+candidates[i],chain,i+1);
            //back
            chain.removeLast();

        }

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值