40 组合总和2-中等

题目:

给定一个候选人编号的集合 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]
]

分析:

这道题是 39 题的强化版,其强化条件是 每个数字在组合中只能使用一次。

这意味,如果原数组中的数字不重复,那么回溯递归的时候要往下一个元素去找。

如果原数组的数字有重复,那么重复的数字只能使用有限次。

所以去重的逻辑很关键,在什么时候去重。

// date 2023/12/26
func combinationSum2(candidates []int, target int) [][]int {
    res := make([][]int, 0, 16)

    var backtrack func(nums []int, target, idx int, temp []int)
    backtrack = func(nums []int, target, idx int, temp []int) {
        if target <= 0 {
            if target == 0 {
                one := make([]int, len(temp))
                copy(one, temp)
                res = append(res, one)
            }
            return
        }
        for i := idx; i < len(nums); i++ {
          	// 这里还没有进入递归回溯的函数
          	// 所以,也就是说,nums[i] 已经搜索以后
          	// 如果存在重复,则跳过
          	// 即对于重复的3个元素,取第一个元素的回溯结果,
          	// 剩下的两个直接跳过,避免结果集中重复
            if i > idx && nums[i] == nums[i-1] {
                continue
            }
          	// 剪枝
            if nums[i] > target {
                break
            }
            temp = append(temp, nums[i])
          	// 元素只能使用一次,从 i+1 开始搜索
            backtrack(nums, target-nums[i], i+1, temp)
            temp = temp[:len(temp)-1]
        }
    }

    sort.Slice(candidates, func(i, j int) bool {
        return candidates[i] < candidates[j]
    })

    backtrack(candidates, target, 0, []int{})

    return res
}

最后更新于