leetcode39组合总数——递归回溯

题目:

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

说明:
candidates 中的数字可以无限制重复被选取
所有数字(包括 target)都是正整数
解集不能包含重复的组合

示例 :
输入: candidates = [2,1,3], target = 3,
所求解集为:
[
[2,1],
[1,1,1],
[3]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum

题目分析

这种搜索题一般都可以暴力搜索(递归),找出所有的组合数,挑选出和为target 的即可,但是很明显这种指数级的增长不是我们想要的,但是我们可以使用一些手段优化递归,例如记忆化搜索,剪枝,使用这种技术可以大大缩短递归调用时间。初步判断这是一道递归+剪枝的题目。做递归题目的第一步就是利用自己的逻辑思维能力和思考能力画出递归的树状图,画的差不多了你对这个题目的理解也就到位了,其次就是涉及好递归函数的参数,递归出口,以及哪里需要剪枝,哪里需要记忆化。

下面我将以上例画出递归树状图
在这里插入图片描述从图中看出符合条件的组合有
[
[2,1],
[1,2],
[1,1,1],
[3]
]
这个结果与 【解集不能包含重复的组合】这个要求不符,解决这一点也很简单,只要做个限制:
即路径上的值只能是非递减的
例如,路径2->2符合,路径2->1不符合,应该剪枝,路径2->3符合条件,路径1->2符合条件,经过这个条件限制后

在这里插入图片描述
标为粉色的路径是非法的,这样就去除了重复组合

代码

public class CombinationSum {
    public static void main(String[] args) {
        CombinationSum cs = new CombinationSum();
        List<List<Integer>> list = cs.combinationSum(new int[]{1,2,3},3);
        for(int i=0;i<list.size();i++){
            for(int j=0;j<list.get(i).size();j++){
                System.out.print(list.get(i).get(j)+" ");
            }
            System.out.println();
        }
    }
    List<List<Integer>> result = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);//排序加快递归速度
        List<Integer> out = new ArrayList<>();
        backTrack(candidates,0,target,0,out);
        return result;
    }
    //start这个参数去除重复组合 并且加快递归速度
    public void backTrack(int[] candidates, int start,int target,int sum,List<Integer> out){
        if(sum==target){//结果集 剪枝
            result.add(new ArrayList<Integer>(out));//这里必须new一个
            return;
        }else{
            for(int i=start;i<candidates.length;i++){
                //肯定不符合,直接break 剪枝
                if (sum + candidates[i] > target) {
                    break;
                }
                out.add(candidates[i]);
                sum+=candidates[i];
                backTrack(candidates,i,target,sum,out);
                //一轮搜索结束 需要移除最后一个元素
                sum-=candidates[i];
                out.remove(out.size()-1);
            }
        }
    }
}

结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值