【LeetCode】解题40:Combination Sum II

本文详细解析LeetCode第40题Combination Sum II的解题思路,采用回溯算法和剪枝策略,通过深度优先搜索在候选数组中找到所有不重复的组合,使得组合内的数相加等于目标值。文章提供了Java实现代码,包括对重复元素的处理,以避免生成重复的组合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem 40: Combination Sum II [Medium]

Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.

Each number in candidates may only be used once in the combination.

Note:

  • All numbers (including target) will be positive integers.
  • The solution set must not contain duplicate combinations.

Example 1:

Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

Example 2:

Input: candidates = [2,5,2,1,2], target = 5,
A solution set is:
[
[1,2,2],
[5]
]

来源:LeetCode

解题思路

与39题类似。
基本思路为回溯算法,采用深度优先,并使用剪枝减少无用搜索。

  • 首先将候选数组按升序排序。
  • 每一轮循环从候选数组中选择一个数x加入组合,比较target'与x的大小,此处target'代表每一轮中新的目标值,根据比较结果分为三种情况:
    a. 如果target' - x = 0,说明组合中sum = target,将该种组合加入最终结果result列表里,并剪枝。
    b. 如果target' - x < 0,说明组合中sum > target,不能再加入任何数,也不能将x替换为更大的候选数,因此这里直接剪枝。
    c. 如果target' - x > 0,说明组合中sum < target,还可以继续深入搜索,更新target' = target' - x,进行递归搜索。
  • 注意1:题目中说每个数只能用一次,因此每次深度搜索时要从下一个坐标(index + 1)开始搜索。
  • 注意2:为了防止重复组合,在backtracking()中的循环内,即广度搜索时,如果下一个数在candidates数组中是重复的,则需要跳过搜索。

要点:回溯算法剪枝

Solution (Java)

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        Arrays.sort(candidates);
        int N = candidates.length;
        if(N == 0) return result;
        List<Integer> comb = new ArrayList<Integer>();
        backtracking(candidates, 0, N, target, comb, result);
        return result;
    }
    private void backtracking(int [] candidates, int index, int N, int target, List<Integer> comb, List<List<Integer>> result){
        int temp = 0;
        for(int i = index; i < N; i++){
        	// skip duplicate candidates
            while(i < N && candidates[i] == temp) i++;
            if(i == N) return;
            temp = candidates[i];
            int next_target = target - candidates[i];
            if(next_target == 0){
                comb.add(candidates[i]);
                result.add(comb);
                return;
            }
            else if(next_target < 0){
                return;
            }
            else{
                List<Integer> next_comb = new ArrayList<Integer>(comb);
                next_comb.add(candidates[i]);
                // search i+1 ~ N-1, candidates[i] may only be used once
                backtracking(candidates, i+1, N, next_target, next_comb, result);
            }
        }
    }
}

修改过程

  • 没有在广度搜索时跳过candidates中重复的数字,导致输入[10,1,2,7,6,1,5]8,输出的集合为:[[1,1,6],[1,2,5],[1,7],[1,2,5],[1,7],[2,6]],其中[1,2,5]和[1,7]都输出了两遍,是因为candidates中有两个1,没有跳过。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值