39. 组合总和
排序后剪枝可以大大减小计算量
class Solution:
def __init__(self):
self.path = []
self.paths = []
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
'''
因为本题没有组合数量限制,所以只要元素总和大于target就算结束
'''
self.path.clear()
self.paths.clear()
# 为了剪枝需要提前进行排序
candidates.sort()
self.backtracking(candidates, target, 0, 0)
return self.paths
def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
# Base Case
if sum_ == target:
self.paths.append(self.path[:]) # 因为是shallow copy,所以不能直接传入self.path
return
# 单层递归逻辑
# 如果本层 sum + condidates[i] > target,就提前结束遍历,剪枝
for i in range(start_index, len(candidates)):
if sum_ + candidates[i] > target:
return
sum_ += candidates[i]
self.path.append(candidates[i])
self.backtracking(candidates, target, sum_, i) # 因为无限制重复选取,所以不是i-1
sum_ -= candidates[i] # 回溯
self.path.pop() # 回溯
40.组合总和II
数层去重,打开新天地的大门了,太妙了,跳过同一树层使用过的元素
class Solution:
def __init__(self):
self.paths = []
self.path = []
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
'''
类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序
'''
self.paths.clear()
self.path.clear()
# 必须提前进行数组排序,避免重复
candidates.sort()
self.backtracking(candidates, target, 0, 0)
return self.paths
def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
# Base Case
if sum_ == target:
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(start_index, len(candidates)):
# 剪枝,同39.组合总和
if sum_ + candidates[i] > target:
return
# 跳过同一树层使用过的元素
if i > start_index and candidates[i] == candidates[i-1]:
continue
sum_ += candidates[i]
self.path.append(candidates[i])
self.backtracking(candidates, target, sum_, i+1)
self.path.pop() # 回溯,为了下一轮for loop
sum_ -= candidates[i] # 回溯,为了下一轮for loop
131.分割回文串
回文串:反转后和原串相同
class Solution:
def __init__(self):
self.paths = []
self.path = []
def partition(self, s: str) -> List[List[str]]:
'''
递归用于纵向遍历
for循环用于横向遍历
当切割线迭代至字符串末尾,说明找到一种方法
类似组合问题,为了不重复切割同一位置,需要start_index来做标记下一轮递归的起始位置(切割线)
'''
self.path.clear()
self.paths.clear()
self.backtracking(s, 0)
return self.paths
def backtracking(self, s: str, start_index: int) -> None:
# Base Case
if start_index >= len(s):
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(start_index, len(s)):
# 此次比其他组合题目多了一步判断:
# 判断被截取的这一段子串([start_index, i])是否为回文串
if self.is_palindrome(s, start_index, i):
self.path.append(s[start_index:i+1])
self.backtracking(s, i+1) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
self.path.pop() # 回溯
else:
continue
def is_palindrome(self, s: str, start: int, end: int) -> bool:
i: int = start
j: int = end
while i < j:
if s[i] != s[j]:
return False
i += 1
j -= 1
return True