代码随想录算法训练营Day 27| 回溯算法part03 | 39. 组合总和、40.组合总和II、131.分割回文串
文章目录
39. 组合总和
一、回溯
class Solution(object):
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
# candidates 中的 同一个 数字可以 无限制重复被选取
# 找出 candidates 中可以使数字和为目标数target的所有 不同组合
result = []
if not candidates:
return result
self.backtracking(candidates,target,0,[],result,0)
return result
def backtracking(self,candidates,target,sum,path,result,startindex):
if sum>target:
return
if sum == target:
result.append(path[:])
return
for i in range(startindex,len(candidates)):
path.append(candidates[i])
sum += candidates[i]
self.backtracking(candidates,target,sum,path,result,i)
sum -= candidates[i]
path.pop()
本题需要注意
- 加上 if sum>target:的剪枝,不然会溢出。
- 因为result 不能有重复组合,所以需要startindex
- 因为可以有重复元素,所以递归的startindex为i 而不是i+1
40. 组合总和 II
与39.组合总和的区别:
本题candidates 中的每个数字在每个组合中只能使用一次。
本题数组candidates的元素是有重复的,而39.组合总和 (opens new window)是无重复元素的数组candidates
一、用used来去重
class Solution(object):
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
path = []
result = []
candidates.sort()
used=[0]*len(candidates)
self.backtracking(candidates,target,0,path,result,0,used)
return result
def backtracking(self,candidates,target,sum,path,result,startindex,used):
if sum>target:
return
if sum == target:
result.append(path[:])
return
for i in range(startindex,len(candidates)):
if i>0 and candidates[i]==candidates[i-1] and used[i-1]==0:
continue
path.append(candidates[i])
sum += candidates[i]
used[i]=1
self.backtracking(candidates,target,sum,path,result,i+1,used)
sum -= candidates[i]
path.pop()
used[i]=0
注意:
1.本题需要将candidates先进行排序
2.树层去重,要去重的是“同一树层上的使用过”,如何判断同一树层上元素(相同的元素)是否使用过了呢。
如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。
3.前一次使用过的元素,如果后面再使用一定会有重复path出现
二、不用used去重
class Solution(object):
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
path = []
result = []
candidates.sort()
self.backtracking(candidates,target,0,path,result,0)
return result
def backtracking(self,candidates,target,sum,path,result,startindex):
if sum>target:
return
if sum == target:
result.append(path[:])
return
for i in range(startindex,len(candidates)):
if i>startindex and candidates[i]==candidates[i-1]:
continue
path.append(candidates[i])
sum += candidates[i]
self.backtracking(candidates,target,sum,path,result,i+1)
sum -= candidates[i]
path.pop()
131.分割回文串
一、组合问题+判断回文
class Solution(object):
def partition(self, s):
'''
递归用于纵向遍历
for循环用于横向遍历
当切割线迭代至字符串末尾,说明找到一种方法
类似组合问题,为了不重复切割同一位置,需要start_index来做标记下一轮递归的起始位置(切割线)
'''
result=[]
self.backtracking(s,0,[],result)
return result
def backtracking(self,s,startindex,path,result):
if startindex == len(s):
result.append(path[:])
return
for i in range(startindex,len(s)):
if self.isPalindrome(s,startindex,i):
path.append(s[startindex:i+1])
self.backtracking(s,i+1,path,result)
path.pop()
def isPalindrome(self,s,start,end):
i = start
j = end
while(i<j):
if s[i]!=s[j]:
return False
i+=1
j-=1
return True
注意点:
- 切割问题可以抽象为组合问题
- 如何模拟那些切割线 :模拟切割线,其实就是index是上一层已经确定了的分割线,i是这一层试图寻找的新分割线
- 切割问题中递归如何终止 :index = 字符串的长度就是切割到最后了即终止
- 在递归循环中如何截取子串 :子串其实就是从index到 i 的那一段字符串
- 如何判断回文:双指针判断