回溯----递归
解决的问题
1. 组合问题
2. 切割问题
3. 子集问题
4. 排列问题
5. 棋盘问题
void backtracking(){
if(终止条件){
收集结果
return;
}
for(集合元素){
处理结点
递归
回溯(撤销之前的)
}
return;
}
组合问题
i+1
i
i+1 & used
tip:
path[:] copy一个path数组,再append进res,这样之后path的改变不会引起res的改变
class Solution:
def __init__(self):
self.path = []
self.res = []
def combine(self, n: int, k: int) -> List[List[int]]:
self.backtracking(n, k, 1)
return self.res
def backtracking(self, n, k, startIndex):
# 1. 递归函数的参数和返回值
# 2. 确定终止条件
if len(self.path) == k:
# 收集结果
self.res.append(self.path[:])
return
# 3. 单层递归逻辑
for i in range(startIndex, n+1):
self.path.append(i)
self.backtracking(n, k, i+1)
self.path.pop() # 回溯
216. Combination Sum III
递归终止条件加1
17. Letter Combinations of a Phone Number
递归:每一层换一个digit-----找到下一层递归的digit
for的每层: 遍历一个digit
class Solution:
def __init__(self):
self.code_map =[ '',
'',
'abc', # 2
'def',
'ghi',
'jkl',
'mno',
'pqrs',
'tuv',
'wxyz' # 9
]
self.path = ''
self.res = []
def letterCombinations(self, digits: str) -> List[str]:
if len(digits) == 0:
return []
self.backtracking(digits, 0)
return self.res
def backtracking(self, digits, index):
if len(self.path) == len(digits):
self.res.append(self.path)
return
digit = int(digits[index])
code_digit = self.code_map[digit] #'abc'
for i in range(len(code_digit)):
self.path += code_digit[i]
self.backtracking(digits, index+1)
self.path = self.path[:-1]
39. Combination Sum
tip
1. 组合问题-----【2,3】和【3,2】没有区别------下一层递归从i开始
2. 可以元素重复----下一层递归可以和上一层用一个元素---从i开始
递归终止条件是sum,pop的时候应该将sum减去pop的元素
class Solution:
def __init__(self):
self.path = []
self.res = []
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
# 组合:2,3 = 3,2
# 可以重复使用
self.backtracking(candidates, target, 0,0)
return self.res
def backtracking(self, candidates, target, index,sum_all):
n = len(candidates)
if sum_all == target:
self.res.append(self.path[:])
return
if sum_all > target:
return
for i in range(index, n):
self.path.append(candidates[i])
sum_all += candidates[i]
self.backtracking(candidates, target, i,sum_all)
sum_all -= candidates[i]
self.path.pop()
40. Combination Sum II
tip
组合问题
每个元素只能用一次,但是给出的元素有重复的
会导致最后结果有重复:[1,7] 和 [7,1] 用的虽然不是同一个1,但是结果重复了
candidates[i] == candidates[i-1]: 去掉【1*,7】, 【1**,7】的重复
used[i-1] == 0: 防止把【1*,1**,7】的也去掉了
class Solution:
def __init__(self):
self.path = []
self.res = []
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
# 关键----去重----树层去重
candidates.sort()
used = [0] * len(candidates)
self.backtracking(candidates, target, 0,0,used)
return self.res
def backtracking(self, candidates, target, index, sum_all, used):
if sum_all == target:
self.res.append(self.path[:])
return
if sum_all > target:
return
for i in range(index, len(candidates)):
if i > 0 and candidates[i] == candidates[i-1] and used[i-1] == 0:
# 防止去掉 上下两层,第一层取第一个1,第二层取第二个1 的i情况
continue
self.path.append(candidates[i])
used[i] = 1
sum_all += candidates[i]
self.backtracking(candidates, target, i+1, sum_all, used )
sum_all -= candidates[i]
used[i] = 0
self.path.pop()
分割问题
131. Palindrome Partitioning
tip
找substring----分割
class Solution:
def __init__(self):
self.path = []
self.res = []
def partition(self, s: str) -> List[List[str]]:
self.backtracking(s, 0)
return self.res
def backtracking(self, s, cutIndex ):
# 切割结束才收集path
if cutIndex == len(s):
self.res.append(self.path[:])
for i in range(cutIndex, len(s)):
cut = s[cutIndex : i+1]
if self.isPal(cut) == True:
self.path.append(cut)
else:
continue
self.backtracking(s, i+1)
self.path.pop()
def isPal(self, s:str):
reverse_s = s[::-1]
return s == reverse_s
子集问题
i+1 & 全部收集
class Solution:
def __init__(self):
self.path = []
self.res = []
def subsets(self, nums: List[int]) -> List[List[int]]:
self.backtracking(nums, 0)
return self.res
def backtracking(self, nums, index):
# 收集结果的时候 每个节点都要收集
self.res.append(self.path[:])
# 但是只有到叶子节点的时候才进入下一层递归
if index >= len(nums):
return
for i in range(index, len(nums)):
self.path.append(nums[i])
self.backtracking(nums, i + 1)
self.path.pop()
class Solution:
# 去重
def __init__(self):
self.path = []
self.res = []
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
nums.sort()
used = [0] * len(nums)
self.backtracking(nums,0, used)
return self.res
def backtracking(self, nums, index, used):
self.res.append(self.path[:])
if index >= len(nums):
return
for i in range(index, len(nums)):
if i > 0 and nums[i] == nums[i-1] and used[i-1] == 0:
continue
self.path.append(nums[i])
used[i] = 1
self.backtracking(nums, i+1,used)
self.path.pop()
used[i] = 0
所谓return就是进入下一层树形结构(进入下一层递归)
所以是只有for循环的index走完本层的时候还会return, 但是每一个节点都要收集到
需要收集每个节点的时候---先收集,再判断终止条件return
491. Non-decreasing Subsequences
不能用sort因为本身数组的顺序不可以改变了
class Solution:
def __init__(self):
self.path = []
self.res = []
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
self.backtracking(nums,0)
return self.res
def backtracking(self, nums, index):
if len(self.path) >= 2:
self.res.append(self.path[:])
if index >= len(nums):
return
uset = set()
for i in range(index, len(nums)):
# 当前元素在本层出现过
if (self.path and nums[i] < self.path[-1]) or nums[i] in uset :
continue
uset.add(nums[i])
self.path.append(nums[i])
self.backtracking(nums, i+1)
self.path.pop()
排列
path去重
path去重 & used
记录当前路径是否用过----同一路径不可以重复
class Solution:
def __init__(self):
self.path = []
self.res = []
def permute(self, nums: List[int]) -> List[List[int]]:
used = [0] * len(nums)
self.backtracking(nums,used)
return self.res
def backtracking(self, nums, used):
if len(self.path) == len(nums):
self.res.append(self.path[:])
return
for i in range(len(nums)):
if used[i]:
continue
self.path.append(nums[i])
used[i] = True
self.backtracking(nums, used)
self.path.pop()
used[i] = False
class Solution:
def __init__(self):
self.path = []
self.res = []
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
# 去重
nums.sort()
used = [0] * len(nums)
self.backtracking(nums,used)
return self.res
def backtracking(self, nums, used):
if len(self.path) == len(nums):
self.res.append(self.path[:])
return
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1] and used[i-1] == 0:
continue
if used[i]:
continue
self.path.append(nums[i])
used[i] = 1
self.backtracking(nums,used)
self.path.pop()
used[i] = 0