leetcode40组合总和 II

题目描述:
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
注意:解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
解题思路:
题目的要求求出所有和为target的组合,并且所有元素只能使用一次,因此可以通过递归加回溯的方法来解决。
使用dfs(pos,rest)表示递归的函数,作用是找到何为target的值。其中pos表示我们当前递归到数组candidate中的pos的个数,rest表示我们还需要选择和为rest的数放入列表作为一个组合。

对于当前第pos的个数,有两种选择,选或者不选。如果选择这个数,那么调用dfs(pos+1,rest-candidate[pos])进行递归,需满足rest=>candidate[pos].如果不选这个数,则调用dfs(pos+1,rest)进行递归。

在递归开始前,如果rest为零,说明找到了一个和为target的组合,将其放入组合中。每次调用递归函数前,如果我们选了那个数,就要将其放入列表的末尾,该列表就存储我们所选择的所有数,就要将其从列表的末尾处删除。
但根据题目要求,列表中不应该存在重复的组合,因此可以将相同的数放在一起处理,通过哈希映射统计数组candidate中每个数出现的次数,并将统计结果放入一个列表freq中。列表freq的长度即为数组candidate中的不同数的个数。其中每一项对应哈希映射中的一个键值对,即每个书以及它出现的次数,在递归时,对于当前第pos的数,它的值就是freq[pos][0],出现的次数为freq[pos][1],则可以调用
dfs(pos+1, rest-i * freq[pos][0])
即选择了这个数i次且freq[pos][0]应小于rest

时间复杂度:O((2^n)*n)
空间复杂度:O(n)

代码实现:

 Arrays.sort(candidates);
        for (int num : candidates){
            int size = freq.size();
            if (freq.isEmpty() || num != freq.get(size -1)[0]){
                freq.add(new int[]{num,1});
            }else {
                ++freq.get(size-1)[1];
            }
        }
        dfs(0,target);
        return ans;
    }

    public void dfs(int pos,int rest){
        if (rest == 0){
            ans.add(new ArrayList<Integer>(sequence));
            return;
        }
        if (pos == freq.size() || rest < freq.get(pos)[0]){
            return;
        }
        dfs(pos+1,rest);
        //剪枝
        int most = Math.min(rest/ freq.get(pos)[0],freq.get(pos)[1]);
        for (int i = 1;i<= most;++i){
            sequence.add(freq.get(pos)[0]);
            dfs(pos+1,rest-i * freq.get(pos)[0]);
        }
        for (int i =1;i <= most;++i){
            sequence.remove(sequence.size() - 1);
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值