刷题使我快乐,满脸开心.jpg
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/combination-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目
给你一个 无重复元素 的整数数组candidates
和一个目标整数target
,找出candidates
中可以使数字和为目标数target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target
的不同组合数少于 150 个。
示例 1:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
示例 2:
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2], target = 1
输出: []
提示:
- 1 <= candidates.length <= 30
- 2 <= candidates[i] <= 40
candidates
的所有元素 互不相同- 1 <=
target
<= 40
思路
这个问题虽然是个中等,但是思路其实很常规,直接回溯莽就好,就当成一个每个节点都有数组长度个数的子节点的树即可。
当然,这个树会有自带的一个剪枝策略,那就是元素要小于当前节点
的目标值。
以candidates = [2,3,6,7], target = 7
为例:
第二个剪枝策略较为隐藏,不过也比较好想出来,那就是去除重复结果,具体实现就是每个节点中,已经选择过的元素,在本节点内后续选择中就不需要再选择了,如图:
只使用第一个剪枝条件,那么就需要后续去重,结果很可能是会超时的,别问我怎么知道的
使用两个剪枝条件后,基本也就趋近于较优解了
代码
双剪枝条件
func combinationSum(candidates []int, target int) [][]int {
usedNums := make([]int, 0)
return do(candidates, usedNums, target)
}
func do(candidates, usedNums []int, target int) [][]int {
res := make([][]int, 0)
for _, num := range candidates {
if num > target {
continue
}
used := false
for _, usedNum := range usedNums {
if num == usedNum {
used = true
}
}
if used {
continue
}
if num == target {
res = append(res, []int{num})
}
resTemp := make([]int, 0)
resTemp = append(resTemp, num)
resLoop := do(candidates, usedNums, target-num)
for _, resLoopItem := range resLoop {
temp := append(resTemp, resLoopItem...)
res = append(res, temp)
}
usedNums = append(usedNums, num)
}
return res
}
单剪枝条件
func combinationSum(candidates []int, target int) [][]int {
res := do(candidates, target)
trueRes := make([][]int, 0)
for _, resItem := range res {
item := resItem
sort.Ints(item)
if len(trueRes) <= 0 {
trueRes = append(trueRes, item)
continue
}
flag := true
for _, temp := range trueRes {
if reflect.DeepEqual(item, temp) {
flag = false
}
}
if flag {
trueRes = append(trueRes, item)
}
}
return trueRes
}
func do(candidates []int, target int) [][]int {
res := make([][]int, 0)
for _, num := range candidates {
if num == target {
res = append(res, []int{num})
}
if num > target {
continue
}
result := make([]int, 0)
result = append(result, num)
resi := do(candidates, target-num)
for _, item := range resi {
temp := append(result, item...)
res = append(res, temp)
}
}
return res
}