day20-数据结构力扣

没有刚开始学习的时候热血了,我也不知道我还能坚持多久

39. 组合总和

题目链接 39. 组合总和 - 力扣(LeetCode)

思路

先贴一个我最开始写的代码,超出时间限制的

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,33,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)

思路

真的很绕,一会不同层,一会去重,一会剪枝

这个题和上题不同之处

  1. 上一题 :同一个数字可以无限制重复选

  2. 这一题 :每个数字只能用一次

  3. 这一题额外 :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()
相关推荐
Web极客码几秒前
如何用三段式确定性剪枝,为 LLM Agent 砍掉 35% 的 Token 成本?
服务器·人工智能·算法·机器学习
程序猿乐锅4 分钟前
【数据结构与算法 | 第六篇】力扣1109,1094差分数组
java·算法·leetcode
wabs66638 分钟前
关于图论【广度优先搜索的理论基础】
算法·图论·宽度优先
孤魂23342 分钟前
逻辑回归算法
算法·机器学习·逻辑回归
weixin_377634841 小时前
【多模型预测】 如何合理融合多个预测分值
算法·机器学习·概率论·预测·agent预测
无风听海1 小时前
Claude Agent Skills 的四种设计模式;从渐进式披露到最小权限
java·算法·设计模式
37.2℃9951 小时前
专业的Claude Design解决方案
前端·算法
め.1 小时前
定点数运算库
算法·unity
yuannl101 小时前
图的深度优先遍历DFS
数据结构
dyxal1 小时前
最长公共子序列(LCS)
python·算法