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()
相关推荐
折哥的程序人生 · 物流技术专研4 小时前
Java面试85题图解版 · 特别篇:2026后端高频面试题复盘(算法底层逻辑+高并发架构设计全解析,附Java实战代码)
java·网络·数据库·算法·面试
想吃火锅10055 小时前
【leetcode】14.最长公共前缀js
算法·leetcode·职场和发展
云絮.6 小时前
数据库操作
数据库·mysql·算法·oracle
小林ixn7 小时前
LeetCode 206. 反转链表(迭代 + 递归详解)
算法·leetcode·链表
凡人叶枫7 小时前
Effective C++ 条款17:以独立语句将 newed 对象置入智能指针
java·linux·开发语言·c++·算法
菜鸟‍8 小时前
LeetCode 1 27 和 704 || 两数之和 移除元素 二分查找
算法·leetcode·职场和发展
退休倒计时9 小时前
【每日一题】LeetCode 142. 环形链表 II TypeScript
算法·leetcode·链表·typescript
popcorn_min10 小时前
Digits 手写数字识别:随机森林多分类 + 像素级特征热力图
算法·随机森林·分类
liulilittle10 小时前
拥塞控制:排水终止的两种决策:OR 与 AND
网络·tcp/ip·计算机网络·算法·信息与通信·tcp·通信
花间相见11 小时前
【LeetCode02】—— 两数之和:哈希表入门经典详解
数据结构·散列表