题一:39. 组合总和
链接
视频总结
关键点
- 可重复选取,若有零则会有死循环,还好题目里没有
编程思路
Me:
- 画图,三部曲,然后一点点的完善!
卡尔:
力扣实战
思路一:
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
#haha这是我独立通过三部曲分析出的第一道回溯题目!!虽然有错误!但都很快找到了改进!开心
res = []
path = []
def func(candidates,target,sum1,startindex):
if sum1>target: #和越界了直接剪枝
return
if sum1==target: #可重复使用,且不限制组成和的个数,所以退出条件只有sum
res.append(path[:])
return
for i in range(startindex,len(candidates)):
sum1 += candidates[i]
path.append(candidates[i])
func(candidates,target,sum1,i) #想想开始处和当前i代表的含义之间的关系,这里的开始处就是当前操作数的索引也就是i,所以不是i+1,不是startindex+1,这些都是在猜,不要猜!要去分析!去找联系,此处可以重复使用,所以是i开始,不能重复使用的则为i+1开始
path.pop()
sum1-=candidates[i]
func(candidates,target,0,0)
return res
#注意去重
# [2,3,6,7]
# 7
# 输出
# [[2,2,3],[2,3,2],[3,2,2],[7]]
# 预期结果
# [[2,2,3],[7]]
# 反思1:
思路二:剪枝
对总集合排序之后,如果下一层的sum(就是本层的 sum + candidates[i])已经大于target,就可以结束本轮for循环的遍历。
在求和问题中,排序之后加剪枝是常见的套路!
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
#haha这是我独立通过三部曲分析出的第一道回溯题目!!虽然有错误!但都很快找到了改进!开心
res = []
path = []
def func(candidates,target,sum1,startindex):
candidates.sort()
if sum1>target:
return
if sum1==target:
res.append(path[:])
return
for i in range(startindex,len(candidates)):
sum1 += candidates[i]
if sum1 > target: # 如果本层 sum + condidates[i] > target,就提前结束遍历,剪枝(此处即使没剪枝,会在刚进入下次递归时就
#直接退出,勉强算是减少了递归的次数,那么递归函数入口的剪枝就没必要了,因为能进来的一定不超过target)
return
path.append(candidates[i])
func(candidates,target,sum1,i)
path.pop()
sum1-=candidates[i]
func(candidates,target,0,0)
return res
题二:40.组合总和II
链接
视频总结
关键点
- 去重数组used
编程思路
Me:
- 去重好像还可以通过一个特殊的set自动去重,(不可以,set只可以对数字自动去除,不能对列表,@day7)
卡尔:
力扣实战
思路一:使用used数组
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
path = []
used=[0]*len(candidates)
candidates.sort() #因为去重时需要判断相邻是否相等,所以需要排序
def func(candidates,startindex,target,sum):
if sum>target:
return
if sum == target:
res.append(path[:])
return
for i in range(startindex,len(candidates)):
if sum+candidates[i]>target:
return
if i>0 and candidates[i]==candidates[i-1] and used[i-1]==0:
continue
sum +=candidates[i]
used[i]=1
path.append(candidates[i])
func(candidates, i+1,target,sum)
path.pop()
sum-=candidates[i]
used[i]=0
func(candidates,0,target,0)
return res
# 反思1:
思路二:不用used数组!奈斯
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
文档总结
1.
131.分割回文串
见anki