算法日常训练9.23

 回溯的题目,常见的有排列,组合,子集,分割,棋盘等,就是将所有的情况都列举出来,加上一些好一点的剪枝操作,套路就是用一个方法然后回溯所有情况,一个一个题目慢慢理解。

class Solution {
    List<List<Integer>> res=new LinkedList<>();//保存结果
    List<Integer> path=new ArrayList<>();//保存每次的路径
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backtracking(candidates,target,0,0);//回溯,sum是当前总和,start是当前开始位置
        return res;
    }
    public void backtracking(int[] candidates,int target,int sum,int start){
        if(sum>target)//如果sum大于了target直接返回
        return;
        if(sum==target)//如果等于,就是一种情况,保存并返回
        {
            res.add(new ArrayList(path));
            return;
        }
        for(int i=start;i<candidates.length;i++){
            path.add(candidates[i]);
            sum+=candidates[i];
            backtracking(candidates,target,sum,i);//从i开始,因为可以有重复的数
            path.remove(path.size()-1);//回溯,移除路径的最后一个元素,并减去他的值
            sum-=candidates[i];
        }
    }
}

 和组数总和1基本换汤不换药,但是还是有几个细节问题需要处理。

第一:为什么需要排序,因为需要去重,把相同的元素放一起利于去重,例如

1 1 2 5 6 7 10   traget=8

这个数组中,在第一层1中已经有了1,2,5这个答案,在第二个1就不需要这个答案,而且第一个1的一层有1,1,6这个答案,所以不用担心第二层的1需要第一层的1。

第二:if(i>0&&candidates[i]==candidates[i-1]&&!used[i-1])

为什么used为false代表前一层访问过,因为回溯,第一层使用时是标志为true,但当他使用完后,被remove,同时标志为false,使用false代表使用过。

第三:i=start+1,这个很简单,因为元素不能重复使用

class Solution {
    List<Integer> path=new ArrayList<>();
    List<List<Integer>> res=new ArrayList<>();
    boolean[] used;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        used=new boolean[candidates.length];
        backtracking(candidates,target,0,0);
        return res;
    }
    public void backtracking(int[] candidates,int target,int sum,int start){
        if(sum>target) return;
        if(sum==target){
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i=start;i<candidates.length;i++){
            if(i>0&&candidates[i]==candidates[i-1]&&!used[i-1])
            continue;
            used[i]=true;
            path.add(candidates[i]);
            
            backtracking(candidates,target,sum+candidates[i],i+1);
            
            used[i]=false;
            path.remove(path.size()-1);
        }
    }

}

  

 和第一个更像,基本就是在一个固定数组找固定大小的组合,这里说一下为什么

backtracking(k,n,i+1,sum+i);等价于sum+=i;backtracking(k,n,i+1,sum);sum-=i;

因为sum+i只是把值传到下一层,这一层的值没变,就等价于加完再传值再回溯

class Solution {
    List<Integer> path=new ArrayList<>();
    List<List<Integer>> res=new ArrayList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtracking(k,n,1,0);
        return res;
    }
    public void backtracking(int k,int n,int start,int sum){
        if(sum>n||path.size()>k) return;
        if(sum==n&&path.size()==k){
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i=start;i<=9;i++){
            path.add(i);
            backtracking(k,n,i+1,sum+i);
            path.remove(path.size()-1);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值