回溯算法解决排列组合问题

解题框架

步骤

  1. 路径:也就是已经做出的选择
  2. 选择列表:也就是你当前可以做的选择
  3. 结束条件:也就是到达决策树底层,无法再做选择的条件

框架代码

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return

    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

39. 组合总和

题目

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。
链接:https://leetcode-cn.com/problems/combination-sum

解题思路

  • 回溯框架
  • 由于可以使用重复元素,每一次搜索的时候设置 下一轮搜索的起点 start
  • 从每一层的第 2 个结点开始,都不能再搜索产生同一层结点已经使用过的 candidate 里的元素

代码

class Solution {
   
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
   
        List<Integer> track = new ArrayList<>();
        int sum = 0;
        backTrack(candidates, target, track, sum, 0);
        return res;
    }

    /**
     * @param candidates 候选数组
     * @param start      搜索起点
     * @param target     目标值
     * @param track      从根结点到叶子结点的路径,是一个栈
     * @param sum        已求和
     */
    void backTrack(int[] candidates, int target, List<Integer> track, int sum, int start) {
   
        // 满足条件,返回
        if(sum == target) {
   
            res.add(new ArrayList(track));
            return;
        }
        for(int i = start;i < candidates.length;i++) {
   
            // 大于target直接返回
            if(sum + candidates[i] > target)
                continue;
            track.add(candidates[i]);
            // 注意:由于每一个元素可以重复使用,下一轮搜索的起点依然是 i
            backTrack(candidates, target, track, sum + candidates[i], i);
            track.remove(track.size() - 1);
        }
    }
}

40. 组合总和 II

题目

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

  • 所有数字(包括目标数)都是正整数。
  • 解集不能包含重复的组合。

解题思路

回溯框架

  • 存在重复元素,为了去重,先对数组排序,遇到重复元素就跳过循环

  • 剪枝条件

    • target - candidates[i] < 0,由于数组递增,所以直接跳出循环
    • 对重复的情况进行剪枝,当candidates[i] == candidates[i-1]时跳过,防止数组越界,i > start

代码

class Solution {
   
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
   
        List<List<Integer>> res = new ArrayList<>();
        Deque<Integer> track = new ArrayDeque<>();
        Arrays.sort(candidates);
        backTrack(candidates, res, track, target, 0);
        return res;
    }
	/**
     * @param candidates 候选数组
     * @param res      	 结果集
     * @param target     剩余值
     * @param track      从根结点到叶子结点的路径,是一个栈
     * @param sum        已求和
     */
    void backTrack(int[] candidates, List<List<Integer>> res, Deque<Integer> track, int target, int start) {
   
        if(target == 0) {
   
            res.add(new ArrayList(track));
            return;
        }
        for(int i = start;i < candidates.length;i++) {
   
            if(target - candidates[i] < 0)
                break;
            // 同一层相同数值的结点,从第 2 个开始,候选数更少,结果一定发
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值