开始学习回溯。
回溯三部曲:
- 递归函数的返回值以及参数
- 回溯函数终止条件
- 单层搜索的过程
77. 组合
class Solution(object):
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
res = [] # 一个二维数组,用于收集结果
self.backtracking(n, k, 1, [], res)
return res
def backtracking(self, n, k, startindex, path, res): # startindex是为了控制搜索的起始位置,防止出现重复,如[1, 2], [2, 1];path是为了收集每一个组合
if len(path) == k: # 终止条件
res.append(path[:])
return
for i in range(startindex, n+1): # for循环是用于横向遍历,递归用于纵向遍历
path.append(i)
self.backtracking(n, k, i+1, path, res)
path.pop() # 要把之前的元素弹出才能放下一个
回溯法还是有些抽象的,感觉和递归一样不能仔细想,只要做好三部曲就行了。
组合优化
如果for循环选择的起始位置后还剩下的元素比还需要的元素少了,就没有必要继续遍历了。
已选择的元素个数:len(path)
列表中还剩下的元素个数:n-i
还需要的元素个数:k-len(path)
n-i+1 >= k-len(path) --> i <= n-(k-len(path))+1
在集合n中至多从该起始位置:i <= n - (k - path.size()) + 1,开始遍历
这个地方为什么要加一其实不太明白,可以举个例子(应该是因为i也包括在内)
class Solution(object):
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
res = [] # 一个二维数组,用于收集结果
self.backtracking(n, k, 1, [], res)
return res
def backtracking(self, n, k, startindex, path, res): # startindex是为了控制搜索的起始位置,防止出现重复,如[1, 2], [2, 1];path是为了收集每一个组合
if len(path) == k: # 终止条件
res.append(path[:])
return
for i in range(startindex, n-(k-len(path))+2): # for循环是用于横向遍历,递归用于纵向遍历
path.append(i)
self.backtracking(n, k, i+1, path, res)
path.pop()