如何去掉一个数组中重复的元素,除了使用哈希表以外,我们还可以先对数组升序排序,重复的元素一定不是排好序以后的第 1 个元素和相同元素的第 1 个元素。根据这个思想,我们先对数组升序排序是有必要的。候选数组有序,对于在递归树中发现重复分支,进而“剪枝”也是有效的。
题目
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例
示例 1: | 输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
|
示例 2: | 输入: candidates = [2,5,2,1,2], target = 5, 所求解集为: [ [1,2,2], [5] ]
|
考察知识点
数组、回溯算法、hash表
核心思想
方法一、加法回溯加重复判断
主要难点有两个:1、一个数字本身不能重复用。2、去掉重复的组合。
如何去掉一个数组中重复的元素,除了使用哈希表以外,我们还可以先对数组升序排序,重复的元素一定不是排好序以后的第 1 个元素和相同元素的第 1 个元素。根据这个思想,我们先对数组升序排序是有必要的。候选数组有序,对于在递归树中发现重复分支,进而“剪枝”也是有效的。所以,这道题和39题非常像,先排序,再剪枝,但是由于禁止每一个数字出现多次,只能出现一次,所以,在剪枝后面,再加一个重复数字判断就行。
| if index > begin and candidates[index - 1] == candidates[index]: continue
|
4.png
为何说“黄色框住部分的 2''
与前一个分支的 2'
在数值上相等,而前一个分支(也就是 2'
)考虑的数组范围更大”?这是因为2'
在输入的candidates [1, 2', 2'', 2''', 5]
中处于最靠前面,其后面的数字,在接下来的子节点都会被考虑到,而 2''
在 2'
的后面,所以2''
考虑的范围一定会小一点,肯定会和前面出现重复,所以黄色框部分要去掉。
大佬题解
方法二
除了用if语句判断以外,还可以用hash表记录出现次数,来防止重复。
待完成
Python版本
方法一的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| class Solution: def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: def backtrack(candidates, begin, path, res, length): if sum(path) == target: res.append(path[:]) return for index in range(begin, length): if (sum(path) + candidates[index]) > target: break if index > begin and candidates[index-1] == candidates[index]: continue path.append(candidates[index]) backtrack(candidates, index+1, path, res, length) path.pop()
length = len(candidates) if length == 0: return [] candidates.sort() path = [] res = [] backtrack(candidates, 0, path, res, length) return res
print("leet code accept!!!") Input = [[10,1,2,7,6,1,5], [2,5,2,1,2]] Input1 = [8, 5] Answer = [ [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ], [ [1,2,2], [5] ] ]
if __name__ == "__main__": solution = Solution() for i in range(len(Input)): print("-"*50) reslut = solution.combinationSum2(Input[i], Input1[i]) print(reslut) print(Answer[i])
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Solution: def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: if not candidates: return [] candidates.sort() n = len(candidates) res = [] def backtrack(i, tmp_sum, tmp_list): if tmp_sum == target: res.append(tmp_list) return for j in range(i, n): if tmp_sum + candidates[j] > target : break if j > i and candidates[j] == candidates[j-1]:continue backtrack(j + 1, tmp_sum + candidates[j], tmp_list + [candidates[j]]) backtrack(0, 0, []) return res
|