leetcode回溯问题
作者:互联网
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。
回溯是递归的副产品,只要有递归就会有回溯。所以回溯函数也就是递归函数,指的都是一个函数
因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。
回溯法解决的问题都可以抽象为树形结构
回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。
for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。
组合问题
(1)77. 组合
可以剪枝的地方就在递归中每一层的for循环所选择的起始位置。
如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了。
优化过程如下:
已经选择的元素个数:path.size();
还需要的元素个数为: k - path.size();
在集合n中至多可以从该起始位置 : n - (k - path.size()) + 1,开始遍历
为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。(python中是+2,因为右边是开区间)
举个例子,n = 4,k = 3, 目前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。
从2开始搜索都是合理的,可以是组合[2, 3, 4]。
res = [] #存放符合条件结果的集合
path = [] #用来存放符合条件结果
def backtrack(n, k, startIdx):
if len(path) == k:
res.append(path[:])
return
for i in range(startIdx, n - (k - len(path)) + 2): #剪枝优化的地方
path.append(i) #处理节点
backtrick(n, k, i + 1) #递归
path.pop() #回溯,撤销处理的节点
backtrick(n, k, 1)
return res
(2)216. 组合总和 III
注意:与77. 组合 区别之一是本题集合固定的就是9个数[1,...,9],所以for循环固定i<=9
剪枝:
- 已选元素总和如果已经大于n(图中数值为4)了,那么往后遍历就没有意义了,直接剪掉。
- for循环的范围也可以剪枝,i <= 9 - (k - path.size()) + 1就可以了。
def __init__(self):
self.res = []
self.path = []
self.curSum = 0
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
self.backtrack(k, n, 1)
return self.res
def backtrack(self, k, n, startIdx):
if self.curSum > n: # 剪枝
return
if len(self.path) == k: # len(path)==k时不管sum是否等于n都会返回
if self.curSum == n:
self.res.append(self.path[:])
return
for i in range(startIdx, 9 - (k - len(self.path)) + 2): # 剪枝
self.path.append(i)
self.curSum += i
self.backtrack(k, n, i + 1)
self.path.pop()
self.curSum -=i
(3)17. 电话号码的字母组合
注意:本题每一个数字代表的是不同集合,也就是求不同集合之间的组合,而77. 组合 和216.组合总和III 都是是求同一个集合中的组合!
参数指定是有题目中给的string digits,然后还要有一个参数就是int型的index。
注意这个index可不是 77.组合和216.组合总和III 中的startIndx了。
而是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index也表示树的深度。
class Solution:
def __init__(self):
self.res = []
self.path = ''
self.letter_map = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz',
}
def letterCombinations(self, digits: str) -> List[str]:
self.res.clear()
if not digits:
return []
self.backtrack(digits, 0)
return self.res
def backtrack(self, digits, idx):
if idx == len(digits): # 当遍历穷尽后的下一层时
self.res.append(self.path)
return
# 单层递归逻辑
letters = self.letter_map[digits[idx]]
for letter in letters:
self.path += letter #处理节点
self.backtrack(digits, idx + 1) #递归
self.path = self.path[: -1] #回溯,撤销处理的节点
(4)
(5)
标签:digits,return,res,self,问题,回溯,path,leetcode 来源: https://www.cnblogs.com/ttyangY77/p/16266577.html