class Solution:
def __init__(self):
self.path = []
self.result = []
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
return self.bcktracking(candidates, target, 0)
def bcktracking(self, candidates, target, startindex):#step1
#step2
if sum(self.path) > target:#剪枝
return
if sum(self.path) == target:
self.result.append(self.path[:]) #浅拷贝
return
#step3
for i in range(startindex, len(candidates)):
self.path.append(candidates[i])
self.bcktracking(candidates, target, i) #每个元素可重复使用,那么i就不需要像#216那样+1了
self.path.pop()
return self.result
class Solution:
def __init__(self):
self.path = []
self.result = []
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
return self.bcktracking(candidates, target, 0)
def bcktracking(self, candidates, target, startindex):#step1
#step2
if sum(self.path) > target:#剪枝
return
if sum(self.path) == target:
self.result.append(self.path[:]) #浅拷贝
return
#step3
for i in range(startindex, len(candidates)):
if sum(self.path) > target: #剪枝,由于对candidates进行排序,那么每添加一个元素进入self.path,就可以check一下是否sum已经大于target了,如果是,那么后面的数只会更大,也就没有必要继续搜索了。
return
self.path.append(candidates[i])
self.bcktracking(candidates, target, i) #每个元素可重复使用,那么i就不需要像#216那样+1了
self.path.pop()
return self.result
- 把所有组合求出来,再用set或者map去重,这么做很容易超时!
- 题目要求,元素在同一个组合内是可以重复的,但两个组合不能相同。因此,要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重。
- 树层去重的话,需要对数组排序!
回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili
class Solution(object):
def __init__(self):
self.path = []
self.result = []
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
candidates.sort()
used = [0]*len(candidates)
return self.backtracking(candidates, target, 0, used)
def backtracking(self, candidates, target, startIndex, used): #step1: used数组用来标记用过的元素
#step2
if sum(self.path) > target:
return
if sum(self.path) == target:
self.result.append(self.path[:]) #浅拷贝
return
#step3
for i in range(startIndex, len(candidates)):
#for loop是树层循环,而去重也是对树层去重,因此接下来就是去重操作
if (i > 0 and candidates[i] == candidates[i-1]) and used[i-1] == 0:
continue
used[i] = 1
self.path.append(candidates[i])
self.backtracking(candidates, target, i+1, used)
self.path.pop()
used[i] = 0
return self.result
class Solution:
def __init__(self):
self.path = []
self.result = []
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
return self.backtracking(candidates, target, 0)
def backtracking(self, candidates, target, startIndex):#step1
#step2
if sum(self.path) == target:
self.result.append(self.path[:])#浅拷贝
return
#step3
for i in range(startIndex, len(candidates)):
if sum(self.path) > target: #剪枝
return
# 跳过同一树层使用过的元素
# 注意:这一步的关键在于candidates要排序!
if i > startIndex and candidates[i] == candidates[i-1]:
continue
self.path.append(candidates[i])
self.backtracking(candidates, target, i+1)
self.path.pop() #回溯
return self.result
在回溯三部曲的step3中,for i in range(startIndex, len(s)), 这样咋一看区间 [startIndex, i] 没有意义,因为i就是startIndex。实际上并不是这样,注意i是要 i+=1的。因此,s[startIndex, i] 就是需要判断是否是回文的子串。
class Solution:
def __init__(self):
self.path = []
self.result = []
def partition(self, s: str) -> List[List[str]]:
return self.backtracking(s, 0)
def backtracking(self, s, startIndex):#step1
#step2
if startIndex == len(s):
self.result.append(self.path[:]) #又忘记需要浅拷贝了
return
#step3
for i in range(startIndex, len(s)):
if self.isPalindrome(s, startIndex, i):
self.path.append(s[startIndex: i+1])
self.backtracking(s, i+1)
self.path.pop() #回溯
else:
continue
return self.result
def isPalindrome(self, str, start, end):
while start < end:
if str[start] != str[end]:
return False
start += 1
end -= 1
return True
错误点:
- 又忘记需要浅拷贝了(由于回溯的原因)
- 递归和回溯这两步需要在判断子串是否回文后给出(之前写在了if...else外)