最容易想到的:k层for循环。
显然不能写那么多层for循环,所以该方法pass
使用回溯法:
用递归解决嵌套层数的问题
n相当于树的宽度,k相当于树的深度。
找到最深处的叶子节点即为找到一个结果,把结果收集起来就是最终答案。
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 + 1): # 需要优化的地方
path.append(i) # 处理节点
self.backtracking(n, k, i + 1, path, result)
path.pop() # 回溯,撤销处理的节点
剪枝优化:
可以剪枝的地方就在递归中每一层的for循环所选择的起始位置。
如果for循环选择的起始位置之后的元素个数已经不足需要的元素个数了,那就没必要搜索了。
优化过程:
为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。
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() # 回溯,撤销处理的节点
找到和为n的k个数的组合,且k在1~9之间
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
result = [] # 存放结果集
self.backtracking(n, k, 0, 1, [], result)
return result
def backtracking(self, targetSum, k, currentSum, startIndex, path, result):
if currentSum > targetSum: # 剪枝操作
return # 如果path的长度等于k但currentSum不等于targetSum,则直接返回
if len(path) == k and currentSum == targetSum:
result.append(path[:])
return
for i in range(startIndex, 10): # 剪枝优化
currentSum += i
path.append(i) # 处理节点
self.backtracking(targetSum, k, currentSum, i + 1, path, result)
currentSum -= i
path.pop() # 回溯,撤销处理的节点
剪枝优化:
已选元素总和如果已经大于n了,那么往后遍历就没有意义了,直接剪掉。
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
result = [] # 存放结果集
self.backtracking(n, k, 0, 1, [], result)
return result
def backtracking(self, targetSum, k, currentSum, startIndex, path, result):
if currentSum > targetSum: # 剪枝操作
return # 如果path的长度等于k但currentSum不等于targetSum,则直接返回
if len(path) == k and currentSum == targetSum:
result.append(path[:])
return
for i in range(startIndex, 9 - (k - len(path)) + 2): # 剪枝优化
currentSum += i
path.append(i) # 处理节点
self.backtracking(targetSum, k, currentSum, i + 1, path, result)
currentSum -= i
path.pop() # 回溯,撤销处理的节点
在一开始判断的时候不能把if currentSum > targetSum
写在if len(path) == k
里面。如果写在里面,就忽略掉了currentSum > targetSum && len(path) != k
的情况。
使用map
或定义一个二维数组,实现数字和字母的映射
def __init__(self):
self.letterMap = [
"", # 0
"", # 1
"abc", # 2
"def", # 3
"ghi", # 4
"jkl", # 5
"mno", # 6
"pqrs", # 7
"tuv", # 8
"wxyz" # 9
]
self.result = [] # 记录结果
self.s = "" # 字符串s来收集叶子节点的结果
完整代码:
class Solution:
def __init__(self):
self.letterMap = [
"", # 0
"", # 1
"abc", # 2
"def", # 3
"ghi", # 4
"jkl", # 5
"mno", # 6
"pqrs", # 7
"tuv", # 8
"wxyz" # 9
]
self.result = []
self.s = []
def backtracking(self, digits, index):
if index == len(digits):
self.result.append("".join(self.s))
return
digit = int(digits[index])
letters = self.letterMap[digit]
for i in range(len(letters)):
self.s.append(letters[i])
self.backtracking(digits, index + 1)
self.s.pop()
def letterCombinations(self, digits: str) -> List[str]:
if len(digits) == 0:
return self.result
self.backtracking(digits, 0)
return self.result
由于题目中限定了2~9,所以并未考虑0和1没有对应字母的情况。在实际问题中应当考虑到。
做了这几道题后,发现它们的解题代码都有共通之处,于是自己总结了一下。
def __init__(): # 需要的时候才写
# 定义全局变量
def backtracking(self, 参数1, 参数2, ...):
# 回溯算法
if 相等:
result.append()
return
for ...:
# 回溯代码
self.backtracking() # 递归
# 回溯代码
def function():
# 排除某些情况
self.backtracking() # 递归
return result