LeetCode39-组合总和

LeetCode39-组合总和

一、题目描述

在这里插入图片描述

二、题目分析

2.1 回溯树

组合问题是典型的需要使用回溯算法求解的问题,在处理回溯算法时,可以先思考该问题的回溯树的结构,并尝试绘制问题的回溯树,绘制回溯树的时候,要注意到题目中的一个特殊要求:同一个数字可以无限制重复选取,下面是回溯树的图示(以[2,3,6,7]为例):

在这里插入图片描述

2.2 递归终止条件

根据题目很容易想到至少有两个终止条件:
(1)当前遍历路径上的和等于target的时候,那么进行结果收集,并且不在进行下一层的递归
(2)当前遍历路径上的和大于target的时候,那么这条路径可以被直接剪枝,不在进行下一层的递归

三、基本代码实现

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        solve(candidates, target, res, new ArrayList<>(), 0, 0);
        return res;
    }
    
    // curRes存放当前路径中的结点
    // curSum存放当前路径结点的和
    // res用来收集所有满足条件的序列
    public void solve(int[] candidates, int target, List<List<Integer>> res,List<Integer> curRes, int startIndex, int curSum) {
            if (curSum == target) {
                res.add(new ArrayList<>(curRes));
                return;
            }

            if (curSum > target) {
                return;
            }

            for (int i = startIndex; i < candidates.length; i += 1) {
                curSum += candidates[i];
                curRes.add(candidates[i]);
                solve(candidates, target, res, curRes, i, curSum);
                curSum -= candidates[i];
                curRes.remove(curRes.size() - 1);
            }
    }
}

四、进一步优化

上述给出的两个递归终止条件只能算是回溯算法的基本限界条件,用来终止递归,但是我们并没有给出回溯算法的剪枝条件,curSum > target只能剪掉同一路径下的分枝,不能剪掉同一层上的分支,其实在回溯的过程中,当我们需要扩展同一层的结点的时候(即for循环的时候),如果同一层的结点时升序扩展的(只需要将原数组升序排序即可),那么我们也可以进行剪枝,剪枝的条件依然是curSum > target,优化后的代码如下:

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(candidates);
        solve(candidates, target, res, new ArrayList<>(), 0, 0);
        return res;
    }

    /**
     * 通过回溯算法求解组合总和问题
     * 
     * @param candidates
     * @param target
     * @param res 存放所有满足条件的结果
     * @param curRes 存放当前搜索路径中满足条件的结果
     * @param startIndex 每一层开始的索引
     */
    public void solve(int[] candidates, 
        int target, List<List<Integer>> res, 
        List<Integer> curRes, int startIndex, int curSum) {
            if (curSum == target) {
                res.add(new ArrayList<>(curRes));
                return;
            }

            if (curSum > target) {
                // 这里只能剪掉同一路径下的分支
                return;
            }

            for (int i = startIndex; i < candidates.length; i += 1) {
                curSum += candidates[i];
                if (curSum > target) {
                    // 剪掉同一层的分支,即for循环不再扩展
                    break;
                }
                curRes.add(candidates[i]);
                solve(candidates, target, res, curRes, i, curSum);
                curSum -= candidates[i];
                curRes.remove(curRes.size() - 1);
            }
    }
}

五、提交结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值