Leetcode 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]
]
提示:
- 1 <= candidates.length <= 100
- 1 <= candidates[i] <= 50
- 1 <= target <= 30
算法思想:
首先要明确本题与组合总和 和 组合总和3的差别。
与组合总和:解集中元素可重复,不限制元素个数。那么下一次迭代范围就是从index开始,而不是从startIndex开始。为什么?元素可重复,为什么不是每次都从1开始呢?如果从1开始,那么必然要包含重复元素。
与组合总和3:解集中无重复元素,限制元素个数。
以上两种方法候选集中没有重复元素。
采用回溯算法。但是给的候选集中可以有重复元素。这是与上两道题的本质区别。
- 回溯函数模板返回值以及参数
path:记录当前路径上的节点
result:记录所有满足条件的组合
startIndex:每个数字在答案中只能使用一次 - 回溯函数终止条件
当path的所有元素的和恰好为target的时候,结束。(可包含剪枝) - 单层搜索逻辑
path中加入节点
递归加入下一个节点
path中出栈
注意避免重复元素,重复元素的来源是题目给的candidates中含有重复元素,而两个重复元素还可以出现在答案结果中。例如:candidates为[1,1,2],target为3的时候,当第一次第一个数取1的时候,第二个数可以取[1,2]。当第二次第一个数取1的时候,第二个数只能取2,此时就包含重复的情况,需要去重。所以综上所述,存在着两种重复的情况,第一种是沿着树枝一直到叶子节点,这样即使有重复元素,也是不同元素。第二种是同一层的相同元素,这样就是重复的,因为前边的元素包含了后边重复元素的所有情况。因此,代码实现中借助used数组来记录元素是否访问过。
代码实现:
import copy
class Solution:
def backtracing(self, candidates, target, path, result, startIndex, used):
if sum(path) > target: # 在这里的话,相当于多递归了一次
return
if sum(path) == target:
result.append(copy.deepcopy(path))
return
for i in range(startIndex, len(candidates)):
if i > 0 and candidates[i] == candidates[i-1] and used[i-1] == False:
continue
# 在这里判断当前path里元素和 与 新加入元素的和是否大于 target,相当于是少递归了一个层次
path.append(candidates[i])
used[i] = True
self.backtracing(candidates, target, path, result, i+1, used)
path.pop()
used[i] = False
def combinationSum2(self, candidates, target: int):
used = [False] * len(candidates)
path = []
result = []
startIndex = 0
candidates.sort()
self.backtracing(candidates, target, path, result, startIndex, used)
return result
import unittest
class CombinationSum2Test(unittest.TestCase):
def setUp(self):
self.solution = Solution()
def test_combinationSum2(self):
candidates = [10, 1, 2, 7, 6, 1, 5]
target = 8
expected = [[1, 1, 6], [1, 2, 5], [1, 7], [2, 6]]
result = self.solution.combinationSum2(candidates, target)
self.assertEqual(result, expected)
# candidates = [2, 5, 2, 1, 2]
# target = 5
# expected = [[1, 2, 2], [5]]
# result = self.solution.combinationSum2(candidates, target)
# self.assertEqual(result, expected)
# candidates = [1, 2, 3]
# target = 4
# expected = [[1, 3]]
# result = self.solution.combinationSum2(candidates, target)
# self.assertEqual(result, expected)
if __name__ == '__main__':
unittest.main()