没有刚开始学习的时候热血了,我也不知道我还能坚持多久
39. 组合总和
思路
先贴一个我最开始写的代码,超出时间限制的
python
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res=[]
self.backtrack(candidates,target,[],res)
return res
def backtrack(self,candidates,target,path,res):
if sum(path)==target:
res.append(path[:])
return
for num in candidates: # 错误 1
path.append(num)
self.backtrack(candidates,target-sum(path),path,res) # 错误 2、3
path.pop()
错误 1:每次都从头遍历 candidates → 产生重复组合
比如 [2,3] 和 [3,2] 都会被算进去!因为你每次递归都重新从第一个数开始选。
题目要求:组合不看顺序,[2,3] 和 [3,2] 算同一个!
错误 2:没有判断 sum (path) > target → 无限递归 / 栈溢出
比如 candidates=[2], target=1你会一直加 2 → sum=2 → 超过 target 但不停止 → 死循环。
错误 3:每次都用 sum (path) → 超级慢、效率极低
递归里反复求和,时间复杂度爆炸。正确做法是用减法,而不是每次求和。
修改
python
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
self.backtrack(candidates, target, 0, [], res) # 加 start=0
return res
def backtrack(self, candidates, target, start, path, res):
if target == 0: # 不用 sum(path),用减法更快
res.append(path.copy())
return
# 从 start 开始,不回头 → 去重
for i in range(start, len(candidates)):
num = candidates[i]
if num > target: # 剪枝,避免死循环
continue
path.append(num)
self.backtrack(candidates, target - num, i, path, res) # 传 i,可重复选
path.pop()
-
用 start 保证不回头(去重)
-
用 target - num 减法(高效)
-
用 if num>target 剪枝(防死循环)
-
递归传 i(可重复选)
提交

python
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res=[]
self.backtrack(candidates,target,0,[],res)
return res
def backtrack(self,candidates,target,start,path,res):
if target==0:
res.append(path[:])
return
for i in range(start,len(candidates)):
num=candidates[i]
if target<0:
return
path.append(num)
self.backtrack(candidates,target-num,i,path,res)
path.pop()
40.组合总和II
题目链接40. 组合总和 II - 力扣(LeetCode)
思路
真的很绕,一会不同层,一会去重,一会剪枝
这个题和上题不同之处
-
上一题 :同一个数字可以无限制重复选
-
这一题 :每个数字只能用一次
-
这一题额外 :candidates 里本身有重复数字 ,要保证结果不重复
第一处改动:
上题(可重复选)
python
# 1. 递归传 i,表示还能再选当前数
self.backtrack(candidates, target - num, i, path, res)
本题(不能重复选)
python
# 1. 递归传 i+1,表示下一次只能选下一个数
self.backtrack(candidates, target - num, i+1, path, res)
第二处改动(去重,因为数组有重复数字):
本题
python
# 跳过同层重复元素,避免结果重复
if i > start and candidates[i] == candidates[i-1]:
continue
| 题目 | 递归传什么 | 是否要排序去重 |
|---|---|---|
| 可重复选 | i | 不需要 |
| 不可重复选 | i+1 | 需要(必须排序 + 去重) |
允许的重复:
不同层 → 纵向重复 [1,1,6]
禁止的重复:
同一层 → 横向重复 [1(第一个), ...] 和 [1(第二个), ...]
同层去重,是为了不让结果重复
提交

python
from typing import List
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
candidates.sort() # 必须排序!方便去重
self.backtrack(candidates, target, 0, [], res)
return res
def backtrack(self, candidates, target, start, path, res):
if target == 0:
res.append(path.copy())
return
for i in range(start, len(candidates)):
num = candidates[i]
# 剪枝
if num > target:
continue
# 去重关键:同层不能选相同数字
if i > start and candidates[i] == candidates[i-1]:
continue
path.append(num)
# 重点:传 i+1,不能重复用当前数
self.backtrack(candidates, target - num, i + 1, path, res)
path.pop()
131.分割回文串
题目链接
思路
示例错误写法
python
class Solution:
def partition(self, s: str) -> List[List[str]]:
res=[]
self.backtrack(s,0,[],res)
return res
def backtrack(self,s,start,path,res):
if path!=[] and path==path[::-1]:
res.append(','.join(path))
return
for i in range(start,len(s)):
path.append(s[i])
self.backtrack(s,i+1,path,res)
path.pop()
-
判断的是 整个 path 是不是回文
-
应该判断 当前切的这一小段 s [start:i+1] 是不是回文
-
把字符一个个加进去
path.append(s[i]) -
应该直接切一段 加进去
path.append(s[start:i+1])
提交

python
class Solution:
def partition(self, s: str):
res = []
self.backtrack(s, 0, [], res)
return res
def backtrack(self, s, start, path, res):
# 切割到最后了,保存答案
if start == len(s):
res.append(path.copy())
return
# 从 start 开始切一段
for i in range(start, len(s)):
substr = s[start:i+1] # 切一小段
# 只要这一小段是回文,就继续
if substr == substr[::-1]:
path.append(substr)
self.backtrack(s, i+1, path, res) # 切后面
path.pop()