回溯算法模板及其例题组合总和讲解(java)

回溯算法也是常见的算法,使用回溯法要么是求所有的可行解要么是求最优解。无论哪一种,都需要找出所有的可行解,进行比较。因此回溯法本质上还是深度优先遍历。它是将问题转换成图的深度遍历。然后沿着一个方向遍历,遍历到某个和节点的时候,判断这个节点是否可行,如果可行,则以这个节点为根进行子节点深度搜索。如果不可行,则往后退一步到父节点,然后继续判断,也就是说回溯一般使用递推的方式实现。

有上面可知,首先要将问题转化为图的形式,然后开始遍历,每一个节点的下一个节点不止一个,我们将所有的下一个节点放在一个列表里,就叫做选择列表;而且还需要一个列表记录现在已经走完的路径

而且我们还需要一个结束的条件,满足这个结束条件后,那么这个方向上的路径就被记录下来,并且结束。

此外还有一个选择机制,根据自己所需,进行选择。

综上所述模板伪代码如下

result={}存放结果
void backtrack(此时已经走完的路径,此次可选择的选择列表){
    if 满足结束条件:
        result.add(路径);
        return;//一定要加,这个表示这个方向的结束;
    for(int i=0;i<选择列表长度;i++){//这里要遍历所有的选择,将所有的选择考虑进去
        if 是否满足需求:
            放入已经走完的路径里;
        else 
            continue;
        backtrack(此时已经走完的路径,此时的选择列表);//注意这里的选择列表已经发生了变化,是刚放进路径的节点下一步的所有选择
        撤销上一步的路径;
    }
}

下面我们结合例题来看:

77. 组合

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]

代码如下

class Solution {
    List<List<Integer>> lists=new ArrayList<>(); //用来存放所有的结果
    List<Integer> list=new ArrayList<>();  //这个就是路径,存放被选择过的节点
    public List<List<Integer>> combine(int n, int k) {
        backtrack(k,0,n);
        return lists;
    }
    public void backtrack(int k,int x,int n){  //通过x来记录选择列表
        if(list.size()==k){
            lists.add(new ArrayList<>(list));
            return;
        }
        for(int i=x+1;i<=n;i++){
            list.add(i);
            backtrack(k,i,n);
            list.remove(list.size()-1);
        }
    }
}

39. 组合总和

给定一个无重复元素的数组 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] ]

class Solution {
    List<List<Integer>> lists=new ArrayList<>();
    List<Integer> list= new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtrack(candidates,target,0);
        return lists;
    }
    public void backtrack(int[] candidates,int target,int start){
        if(target==0){
            lists.add(new ArrayList<>(list));
            return;
        }
        if(target<candidates[0]) return;
        for(int i=start;i<candidates.length;i++){
            if(target-candidates[i]>=0){
                list.add(candidates[i]);
                backtrack(candidates,target-candidates[i],i);//注意这里,因为题目是每个元素都可以重复使用,如果不可以																重复,就i+1
                list.remove(list.size()-1);
            }
        }
    }
}

40. 组合总和 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] ]

代码如下

class Solution {
    List<List<Integer>> lists=new ArrayList<>();
    List<Integer> list= new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtrck(candidates,target,0);
        return lists;
    }
    public void backtrck(int[] candidates,int target,int x){
        if(target==0){
            lists.add(new ArrayList<>(list));
            return;
        }
        if(target<candidates[0]) return;
        for(int i=x;i<candidates.length;i++){
            if(target-candidates[i]>=0){
                if(i>x && candidates[i]==candidates[i-1]) continue; //这个要防止重复的组合,比如这里有两个1,都可以和7组合成8
                list.add(candidates[i]);
                backtrck(candidates,target-candidates[i],i+1);//由于元素不可重复使用,所以要下一位
                list.remove(list.size()-1);
            }
        }
    }
}

216. 组合总和 III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

所有数字都是正整数。 解集不能包含重复的组合。 示例 1:

输入: k = 3, n = 7 输出: [[1,2,4]] 示例 2:

输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]]

结合上一题和第一题的组合,可得代码如下

class Solution {
    List<List<Integer>> lists=new ArrayList<>();
    List<Integer> list =new ArrayList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtrack(n,k,1);
        return lists;
    }
    public void backtrack(int n,int k,int x){
        if(list.size()==k&&n==0 ){
            lists.add(new ArrayList<>(list));
            return;
        }
        for(int i=x;i<10;i++){
            list.add(i);
            backtrack(n-i,k,x+1);
            list.remove(list.size()-1);
        }
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值