这题展示了回溯算法在组合中的优化,需要进行去重操作,可以与上一题17. 电话号码的字母组合对比进行解题。其实,大致的思路差不多,但多了去重的操作。
力扣入口:40. 组合总和 II
套用回溯算法的问题模版,需要确定:
- 函数的参数与返回值;
- 终止条件;
- 单层循环逻辑。
函数的参数及返回值:
这里, 我们尝试与电话号码字母组合不同的方式,把结果res
和路径path
当参数传入函数。当然,也可以使用以前的方式写在__init__
里面,具体参考这个。因为结果在参数里面,所以不需要返回值。
def combine(self, candidates, target, path, res):
终止条件和上题一样,和大于目标值以及等于目标值时退出。在等于目标值时,意味着找到了一条路径,我们把它放进结果里面。
提醒要使用path[:]
把路径拷贝下来,而不是直接用path
引用导致结果为[]
。
if sum(path) > target: return
elif sum(path) == target:
res.append(path[:])
return
单层for
循环需要去重,当后一个数字等于前一个数字的时候,我们跳过。以[1, 1, 7], target = 8
为例子,我们在处理第一个1
的时候找到了[1, 8]
组合,那么在遇到第二个1
的时候我们就需要跳过他,避免又找到[1, 8]
。
很明显,这样的去重方式需要重复元素相邻,所以需要对数组进行排序 candidates. Sort()
。
博主也尝试了在找到路径之后再进行去重的方式,但是本题超时了,所以还是使用以上的方式。
for index in range(len(candidates)):
if index != 0 and candidates[index - 1] == candidates[index]:
continue
path.append(candidates[index])
self.combine(candidates[index + 1:], target, path, res)
path.pop()
总结以上代码:
class Solution:
def combine(self, candidates, target, path, res):
if sum(path) > target: return
elif sum(path) == target:
res.append(path[:])
return
for index in range(len(candidates)):
if index != 0 and candidates[index - 1] == candidates[index]:
continue
path.append(candidates[index])
self.combine(candidates[index + 1:], target, path, res)
path.pop()
return
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
res = []
path = []
self.combine(candidates, target, path, res)
return res