给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
解题思路总结
- 回溯法是一种非常有效的组合生成策略。它通过在路径中添加元素,并不断递归深入,再回溯撤销操作,确保能够找到所有可能的组合。
- 优化点在于通过控制循环的范围,避免了不必要的计算,从而提升了算法的效率。通过提前计算可以填入组合的数字范围,代码避免了无效的递归,缩减了计算空间。
完整代码如下:
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
result = [] # 存放结果集
self.backtracking(n, k, 1, [], result)
return result
def backtracking(self, n, k, startIndex, path, result):
if len(path) == k:
result.append(path[:])
return
for i in range(startIndex, n - (k - len(path)) + 2): # 优化的地方
path.append(i) # 处理节点
self.backtracking(n, k, i + 1, path, result)
path.pop() # 回溯,撤销处理的节点
def combine(self, n: int, k: int) -> List[List[int]]:
result = [] # 存放结果集
self.backtracking(n, k, 1, [], result)
return result
combine
方法是程序的入口函数。result
是一个空列表,用来存储所有符合条件的组合结果。- 调用了
backtracking
方法来执行递归和回溯操作,从数字1
开始,逐步构建组合。
def backtracking(self, n, k, startIndex, path, result):
if len(path) == k:
result.append(path[:])
return
for i in range(startIndex, n - (k - len(path)) + 2): # 优化的地方
path.append(i) # 处理节点
self.backtracking(n, k, i + 1, path, result)
path.pop() # 回溯,撤销处理的节点
-
递归终止条件:
if len(path) == k
: 如果当前路径path
的长度达到了k
,表示已经形成了一个完整的组合。result.append(path[:])
: 将当前组合的副本加入到result
中。return
: 结束当前递归。
-
循环部分:
for i in range(startIndex, n - (k - len(path)) + 2)
: 这是一个循环,用来尝试从startIndex
到n
的每一个数字作为组合的一部分。这里的范围设置是一个优化点,确保循环不会无意义地执行。n - (k - len(path)) + 2
确保了剩下的数字足够填满组合。例如,如果k
是 3,而当前path
中已经有 2 个数字,那么剩下的空间只能容纳 1 个数字,因此i
不需要遍历到n
。
-
处理节点:
path.append(i)
: 将当前数字i
加入到路径path
中。
-
递归调用:
self.backtracking(n, k, i + 1, path, result)
: 递归地处理下一个数字,startIndex
更新为i + 1
,表示下一个组合元素只能从i
之后的数字中选取。
-
回溯:
path.pop()
: 在递归返回后,将当前数字i
从路径path
中移除,恢复到递归前的状态,进行下一轮循环。这一步是“回溯”的核心操作。