这题的关键在于去重,重点为通过start_index控制开始遍历的元素不在已经
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
result = []
path = []
def backtracking(start_index):
if sum(path) == target:
result.append(path[:])
return
elif sum(path) > target:
return
for i in range(start_index, len(candidates)):
path.append(candidates[i])
backtracking(i)
path.pop()
backtracking(0)
return result
下面是GPT4对代码的优化
您的代码已经相当清晰和高效了,但还有一些小优化可以实施。主要的优化是减少多次计算
sum(path)
,我们可以通过传递当前的和到递归调用中来减少这些额外的计算。下面是优化后的代码:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: result = [] path = [] def backtracking(start_index, target): if target == 0: result.append(path[:]) return elif target < 0: return for i in range(start_index, len(candidates)): path.append(candidates[i]) backtracking(i, target - candidates[i]) path.pop() backtracking(0, target) return result
在这个版本的代码中,我引入了一个新的参数
target
到backtracking
函数中,用来表示目标值的当前余额。这意味着我们不需要多次计算sum(path)
,因为每次我们添加一个新的数字到
path
,我们只需从target
中减去这个数字。如果target
成为 0,这就意味着我们找到了一个有效的组合。如果
target
变为负数,这就意味着我们已经超过了目标值,我们可以停止进一步的搜索。
下面的代码通过了测试样例,但是超时了,主要在于对连续重复数字怎么减少不必要的重复判断,这块没能想出来
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
result = []
path = []
def backtracking(start_index, rest):
if rest == 0:
if path not in result:
result.append(path[:])
return
elif rest < 0:
return
for i in range(start_index, len(candidates)):
path.append(candidates[i])
backtracking(i+1, rest-candidates[i])
path.pop()
backtracking(0, target)
return result
GPT4的优化如下
你的当前解决方案包含一些冗余的步骤,尤其是检查
path
是否已经在result
中。这个操作是 O(n)
的时间复杂度,这使得整个算法的时间复杂度过高。而且,我们可以简单地跳过重复的数字来消除结果中的重复组合,从而避免了对结果列表的查找。以下是修改后的代码,改进了这两点,降低了时间复杂度:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: candidates.sort() result = [] path = [] def backtracking(start_index, rest): if rest == 0: result.append(path[:]) return elif rest < 0: return for i in range(start_index, len(candidates)): # 跳过相同的元素,避免结果中的重复 if i > start_index and candidates[i] == candidates[i-1]: continue path.append(candidates[i]) backtracking(i+1, rest-candidates[i]) path.pop() backtracking(0, target) return result ``` 在这个修改过的版本中,我们在开始新的递归调用之前,检查当前的元素是否和上一个元素相同。如果是,我们就跳过这个元素,以此来避免在结果中生成重复的组合。这使得我们无需在将 `path` 添加到 `result` 之前检查 `path` 是否已经在 `result` 中。
这部分自己之前写的判断条件是 if i > 0 and candidates[i] == candidates[i-1]:
,这样的话会把满足条件的连续数组给去掉,而使用i>startindex则保证了当前的连续的下一个是可以被遍历的。
这题是看了视频才理解,这里是得到所有符合题意的切割方案,将start_index作为切割点,在切割之前就先判断是否符合回文串,所以在最后保存结果的时候条件为切割点在字符末尾,而不用判断是否满足题意。自己按照该思路撰写的代码如下
class Solution:
def partition(self, s: str) -> List[List[str]]:
def is_huiwen(sub_s):
return sub_s == sub_s[::-1]
result = []
path = []
n = len(s)
def backtracking(start_index):
if start_index >= n:
result.append(path[:])
return
for i in range(start_index+1, n+1):
sub_s = s[start_index:i]
if is_huiwen(sub_s):
path.append(sub_s)
backtracking(i)
path.pop()
backtracking(0)
return result
参考代码如下
class Solution:
def partition(self, s: str) -> List[List[str]]:
result = []
self.backtracking(s, 0, [], result)
return result
def backtracking(self, s, start_index, path, result ):
# Base Case
if start_index == len(s):
result.append(path[:])
return
# 单层递归逻辑
for i in range(start_index, len(s)):
# 若反序和正序相同,意味着这是回文串
if s[start_index: i + 1] == s[start_index: i + 1][::-1]:
path.append(s[start_index:i+1])
self.backtracking(s, i+1, path, result) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
path.pop() # 回溯
start_index的控制、去重以及回溯前的条件判断是今天题目的精髓
代码随想录算法训练营第二十七天 | 39. 组合总和、40.组合总和II、131.分割回文串_小蛙先森的博客-CSDN博客