文章目录
一、组合问题
77. 组合:n个数中求k个数的组合问题
- 思路:每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围。终止条件:每次搜索到了叶子节点,我们就找到了一个结果。for循环用来横向遍历,递归的过程是纵向遍历。backtracking(递归函数)通过不断调用自己一直往深处遍历,总会遇到叶子节点,遇到了叶子节点就要返回。
- 参数含义:
n:相当于树的宽度
k:相当于树的深度。
startIndex:这个参数用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,…,n] )
len(path):已经选择的元素个数
k-len(path):还需要的元素个数
- 剪枝优化:
可以剪枝的地方就在递归中每一层的for循环所选择的起始位置。如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了。
for i in range(StartIndex, n-(k-len(path)) + 2): #
左闭右开,在集合n中至多要从该起始位置 : n - (k - len(path) + 1,开始遍历
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
res = [] # 存放符合条件结果的集合
path = [] # 用来存放符合条件单一结果
def backtrack(n, k, StartIndex): # 回溯(递归)
if len(path) == k: # 终止条件
res.append(path[:]) # 存放结果
return
for i in range(StartIndex, n-(k-len(path)) + 2): # 左闭右开,在集合n中至多要从该起始位置 : n - (k - len(path) + 1,开始遍历
path.append(i) # 处理节点
backtrack(n, k, i+1) # 递归:控制树的纵向遍历,注意下一层搜索要从i+1开始
path.pop() # 回溯,撤销处理的节点
backtrack(n, k, 1)
return res
- 树的宽度为9,深度为k
- 优化剪枝:(1)目前求得的和大于n,则剪枝(2)遍历时startindex至多遍历到9-(k-len(path))+1
- 对于该层上的每个结点,都要进行以下步骤:处理节点–递归–回溯
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
res=[]
path=[]
def backtrack(startindex,sum,n,k):
if sum>n:return # 剪枝
if sum==n and len(path)==k:res.append(path[:]) # 终止条件:走到叶子结点且和为n
for i in range(startindex,9-(k-len(path))+2): # 宽度遍历
# 处理结点
path.append(i)
sum+=i
# 递归
backtrack(i+1,sum,n,k) # 深度遍历
# 回溯
path.pop()
sum-=i
backtrack(1,0,n,k)
return res