39. 组合总和 - 力扣(LeetCode)
题目要求:可以无限制重复被选取;如下图所示:
本题仅有总和的限制,所以没有递归层数的限制,只要选取的元素总和超过target就返回;
from typing import List
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
self.backtracking(candidates, target, 0, 0, [], res)
return res
def backtracking(self, candidates, target, total, startIndex, path, res):
# 当累加值大于目标值,返回
if total > target:
return
if total == target:
res.append(path[:])
return
for i in range(startIndex, len(candidates)):
total += candidates[i]
path.append(candidates[i])
self.backtracking(candidates, target, total, i, path, res)
# 回溯,小于目标值,返回上一层,便于进行下一层的另一个元素进行累加
total -= candidates[i]
# 暂时数据存储中弹出该值
path.pop()
if __name__ == '__main__':
candidates = [2,3,6,7]
target = 7
res = Solution().combinationSum(candidates, target)
print(res)
在看到上图遍历的过程时,就已经想到,为什么要一遍遍判断累加值大于目标值的情况,若一个数值是有序的,若累加到第一个值就大于目标值的话,数组的后序数值就不需要进行累加,直接就可以return,这样不是更方便吗?
下部分代码就是对该部分进行调整后的代码(剪枝):
from typing import List
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
# 对数组进行排序,若累加值大于第一个值时就可以直接return,不需要对后面的值进行计算
candidates.sort()
self.backtracking(candidates, target, 0, 0, [], res)
return res
def backtracking(self, candidates, target, total, startIndex, path, res):
# 当累加值大于目标值,返回
if total == target:
res.append(path[:])
return
for i in range(startIndex, len(candidates)):
# 对累加值进行判断,若大于目标值,直接break,跳出本次循环
if total + candidates[i] > target:
break
total += candidates[i]
path.append(candidates[i])
self.backtracking(candidates, target, total, i, path, res)
# 回溯,小于目标值,返回上一层,便于进行下一层的另一个元素进行累加
total -= candidates[i]
# 暂时数据存储中弹出该值
path.pop()
if __name__ == '__main__':
candidates = [2,3,6,7]
target = 7
res = Solution().combinationSum(candidates, target)
print(res)
40. 组合总和 II - 力扣(LeetCode)
本题要求candidates
中的每个数字在每个组合中只能使用一次 ,我理解为使用该数字后,需要把该数字在candidates中删除,然后再剩余的数字中进行遍历;该思路就是去重的思路,便于后序递归对该值进行重复遍历;那需要一种什么方法来进行去重操作呢?(所谓去重,其实就是使用过的元素不能重复选取,去重的话,要进行数组的排序,便于判断前后值是否是同一个值)
from typing import List
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
candidates.sort()
self.backtracking(candidates, target, 0, 0, [], res)
return res
def backtracking(self, candidates, target, total, startIndex, path, res):
if total == target:
res.append(path[:])
return
for i in range(startIndex, len(candidates)):
# 要对同一树层使用过的元素进行跳过
if i > startIndex and candidates[i] == candidates[i-1]:
continue
if total + candidates[i] > target:
break
total += candidates[i]
path.append(candidates[i])
# 这里是i+1,每个数字在每个组合中只能使用一次(未完全理解)
self.backtracking(candidates, target, total, i+1, path, res)
# 回溯
total -= candidates[i]
path.pop()
if __name__ == '__main__':
candidates = [10,1,2,7,6,1,5]
target = 8
res = Solution().combinationSum2(candidates, target)
print(res)
集合去重的重任就是used来完成的,一个bool型数组,用来记录同一树枝上的元素是否使用过;(该方法还未理解)
from typing import List
# 使用used进行判断去重
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
used = [False] * len(candidates)
res = []
candidates.sort()
self.backtracking(candidates, target, 0, 0, used, [], res)
return res
def backtracking(self, candidates, target, total, startIndex, used, path, res):
if total == target:
res.append(path[:])
return
for i in range(startIndex, len(candidates)):
# 对于向太阳的数字,只选择第一个未被使用的数字,跳过其他相同数字
if i > startIndex and candidates[i] == candidates[i-1] and not used[i-1]:
continue
if total + candidates[i] > target:
break
total += candidates[i]
path.append(candidates[i])
used[i] = True
# 这里是i+1,每个数字在每个组合中只能使用一次(未完全理解)
self.backtracking(candidates, target, total, i+1, used, path, res)
# 回溯
used[i] = False
# 回溯
total -= candidates[i]
path.pop()
if __name__ == '__main__':
candidates = [10,1,2,7,6,1,5]
target = 8
res = Solution().combinationSum2(candidates, target)
print(res)
131. 分割回文串 - 力扣(LeetCode)
首先明白回文串的定义:“回文串”是一个正读和反读都一样的字符串;
切割问题和上一题一个意思,即不能再原来切割过的地方进行重新切割,即要选取剩下的部分进行切割,类似于组合问题:
例如对于字符串abcdef:
- 组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中再选取第三个.....。
- 切割问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中再切割第三段.....。
from typing import List
class Solution:
def partition(self, s: str) -> List[List[str]]:
res = []
self.backtracking(s, 0, [], res)
return res
def backtracking(self, s, start_index, path, res):
if start_index == len(s):
res.append(path[:])
return
for i in range(start_index, len(s)):
# 判断被截取的字符串([start_index, i])是否是回文串;
if self.is_palindrome(s, start_index, i):
path.append(s[start_index:i+1])
self.backtracking(s, i+1, path, res)
path.pop()
def is_palindrome(self,s:str, start:int, end:int) -> bool:
i = start
j = end
while i < j:
if s[i] != s[j]:
return False
i += 1
j -= 1
return True
if __name__ == '__main__':
s = "aab"
res = Solution().partition(s)
print(res)