1.回溯算法基础
文档讲解: 代码随想录
回溯是递归的副产品,有递归就会有回溯,回溯函数也就是递归函数。虽然回溯算法很难理解,但器本质上是穷举,效率不高。一般解决如下几种问题:组合、切割、子集、排列、棋盘。回溯法解决的问题都可以抽象为树形结构,集合的大小构成了树的宽度,递归的深度就构成了树的深度。
回溯三部曲:
(1)回溯函数返回值和参数,一般取名为 backtracking,返回值一般为 void,参数的话,先写逻辑,需要什么参数就填什么参数。
(2)终止条件:一般搜到叶子节点,收集结果,结束本层递归。
(3)单层逻辑:
for 选择:本层集合中的元素(树中节点孩子的数量就是集合的大小):
处理节点
backtracking(路劲,选择列表)
回溯,撤销处理结果
2.第77题. 组合
class Solution(object):
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
res = []
path = []
self.backtracking(n, k, 1, path, res)
return res
def backtracking(self, n, k, startindex, path, res):
if len(path) == k:
res.append(path[:])#建立副本
return
for i in range(startindex, n + 1):
path.append(i)
self.backtracking(n, k, i + 1, path, res)
path.pop()
注意点:
(1)combine函数中调用回溯函数的时候,startindex为1,是因为题目中说明了范围为[1,n]。
(2)终止条件中,res添加path的时候要建立副本,不然是引用,res 中的结果会随着path而改变。
(3)range 的是左闭右开,所以要写为n+1。
(4)关于回溯算法,以图为例,为什么会在[1,4]后实现两次pop:因为for循环遍历到 i = 1,利用递归取到[1,4],会将4弹出结束 self.backtracking(n, k, i + 1, path, res),然后再弹出1进入 i = 2 的循环。
剪枝优化版:
当已选取的元素加上剩余可选的元素数量小于 k 的时候,再进行遍历会增加时间复杂度,因此在 for 循环的范围内选取 startindex 到 n - (k - len(path) ) + 1,就可以了。
class Solution(object):
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
res = []
path = []
self.backtracking(n, k, 1, path, res)
return res
def backtracking(self, n, k, startindex, path, res):
if len(path) == k:
res.append(path[:])#建立副本
return
for i in range(startindex, n - (k - len(path)) + 2):
path.append(i)
self.backtracking(n, k, i + 1, path, res)
path.pop()
3.216.组合总和III
题目链接:216.组合总和III
文档讲解: 代码随想录
class Solution(object):
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
res = []
path = []
self.backtracking(k, n, 1, 0, path, res)
return res
def backtracking(self, k, n, startindex, countsum, path, res):
#终止条件
if len(path) == k:
if countsum == n:
res.append(path[:])
return
for i in range(startindex, 9 - (k - len(path)) + 2):
path.append(i)
self.backtracking(k, n, i + 1, countsum + i, path, res)
path.pop()
剪枝:已选元素和大于 n ,往后遍历已经没有意义了,所以直接剪掉。
class Solution(object):
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
res = []
self.backtracking(k, n, 0, 1, [], res)
return res
def backtracking(self, k, n, countsum, startindex, path, res):
if countsum > n:
return
if len(path) == k:
if countsum == n:
res.append(path[:])
return
for i in range(startindex, 9 - (k - len(path)) + 2):
path.append(i)
self.backtracking(k, n, countsum + i, i + 1, path, res)
path.pop()
4.17.电话号码的字母组合
题目链接:17.电话号码的字母组合
文档讲解: 代码随想录
首先要有一个数字与字符串的映射,然后再使用回溯算法进行求解。
class Solution(object):
def __init__(self):
self.lettermap = [
"",#0
"",#1
"abc",#2
"def",#3
"ghi",#4
"jkl",#5
"mno",#6
"pqrs",#7
"tuv",#8
"wxyz"#9
]
self.res = []
self.s = ""
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
if len(digits) == 0:
return self.res
self.backtracking(digits, 0)
return self.res
def backtracking(self, digits, index):
#终止条件
if index == len(digits):
self.res.append(self.s)
return
digit = int(digits[index]) #将索引处的数字转换为整数
letters = self.lettermap[digit] #获取对应的字符集
for i in range(len(letters)):
self.s += letters[i]
self.backtracking(digits, index + 1)
self.s = self.s[:-1] #回溯
精简版:
class Solution(object):
def __init__(self):
self.lettermap = [
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
]
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
if not digits:
return []
res = []
self.backtracking(digits, 0, "", res)
return res
def backtracking(self, digits, index, s, res):
if index == len(digits):
res.append(s)
return
digit = int(digits[index])
letter = self.lettermap[digit]
for i in range(len(letter)):
self.backtracking(digits, index + 1, s + letter[i], res)
使用列表
class Solution(object):
def __init__(self):
self.lettermap = [
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
]
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
if not digits:
return []
res = []
self.backtracking(digits, 0, [], res)
return res
def backtracking(self, digits, index, path, res):
if index == len(digits):
res.append("".join(path))
return
digit = int(digits[index])
letters = self.lettermap[digit]
for i in range(len(letters)):
path.append(letters[i])
self.backtracking(digits, index + 1, path, res)
path.pop()