跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python算法

算法题解:组合总和、组合总和 II 及分割回文串

综述由AI生成详细解析了三个经典回溯算法题目:组合总和、组合总和 II 以及分割回文串。针对每个问题阐述了核心思路,包括如何构建搜索树、进行剪枝优化、处理重复元素去重以及判断回文串。文中提供了完整的 Python 代码实现,并分析了时间复杂度和空间复杂度,旨在帮助开发者掌握回溯法在解决组合与分割类问题中的关键技巧与应用场景。

DebugKing发布于 2026/3/29更新于 2026/6/830 浏览

39. 组合总和

目标: 给你一个数组 candidates(没有重复数),再给一个目标值 target。

  • 从 candidates 里选数(可以重复选同一个数)
  • 让选出来的数之和等于 target
  • 返回所有可能的组合

例子:

candidates = [2, 3, 6, 7], target = 7
结果:[[2, 2, 3], [7]]

核心思路与剪枝

不停地往组合里加数,只要和没超过 target 就继续试,等于 target 就记下来,超过就停。

  1. 用一个 path 记录当前选了哪些数
  2. 用一个 cur_sum 记录当前和
  3. 每一步从 candidates 里选一个数(⚠️ 可以重复选同一个数)
  4. 三种情况
    • cur_sum == target → 存结果
    • cur_sum > target → 这条路不行,停
    • < target → 继续选

代码实现

① '可以重复使用'指的是什么?

当前选中的这个数,下一层还能不能再选一次?能 → 递归传 i;不能 → 递归传 i + 1

② '组合不能有重复'是怎么保证的?

不允许回头选更小的下标,所以 for 循环从当前 start 也就是 i 开始,永远不从 0 重新选。

from typing import List

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        path = []

        def backtracking(start, cur_sum):
            # 如果当前和等于 target,记录结果
            if cur_sum == target:
                res.append(path[:])
                
            
             cur_sum > target:
                
            
             i  (start, (candidates)):
                path.append(candidates[i])
                
                backtracking(i, cur_sum + candidates[i])
                path.pop()

        backtracking(, )
         res
return
# 如果当前和已经超过 target,这条路不可能成功
if
return
# 从 start 开始选,避免组合重复(如 [2,3] 和 [3,2])
for
in
range
len
# 注意:这里还是 i,而不是 i+1,因为同一个数可以重复选
0
0
return
指标复杂度说明
时间复杂度O(S)时间复杂度取决于搜索树所有叶子节点的深度之和,即所有可行解的长度之和
时间复杂度上界O(n · 2ⁿ)一个较松的上界:将问题视为每个位置'选 / 不选',用于给出理论最大界
实际运行时间远小于 O(n · 2ⁿ)由于剪枝条件(如 target - candidates[i] ≥ 0),大量分支不会继续展开
空间复杂度(不含输出)O(target)最坏情况下不断选择最小数,递归深度和 path 长度最多为 target / min(candidates)
输出空间取决于解的数量需要存储所有合法组合,可能非常多,无法用一个简单的 Big-O 上界

40. 组合总和 II

目标: 给你一个数组 candidates(可能有重复数字),再给一个目标值 target。

  • 从 candidates 里选数
  • 每个位置的数只能用一次
  • 组合的和等于 target
  • 返回所有不重复的组合

核心思路

先排序,再回溯;每个数只能用一次,用 i+1;同一层遇到相同的数,只用一次。

去重的关键规则

同一层递归中,如果当前数和前一个数相同,就跳过。

if i > start and candidates[i] == candidates[i - 1]:
    continue
  • i > start:保证是同一层
  • == candidates[i-1]:遇到重复数
  • continue:这一层不再用它

代码实现

from typing import List

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        candidates.sort()  # ① 排序:去重的前提
        res = []
        path = []

        def backtracking(start, cur_sum):
            # 如果当前和等于 target,记录结果
            if cur_sum == target:
                res.append(path[:])
                return
            # 剪枝:当前和已经超过 target
            if cur_sum > target:
                return
            for i in range(start, len(candidates)):
                # ② 去重:同一层不能用相同的数
                if i > start and candidates[i] == candidates[i - 1]:
                    continue
                path.append(candidates[i])
                # ③ i + 1:每个数只能用一次
                backtracking(i + 1, cur_sum + candidates[i])
                path.pop()

        backtracking(0, 0)
        return res
指标复杂度说明
时间复杂度O(n · 2ⁿ)最坏情况下可视为枚举所有子集,每个解构造成本为 O(n)
空间复杂度(不含输出)O(n)递归调用栈深度 + path 长度,最多使用 n 个元素

131. 分割回文串

目标: 给你一个字符串 s,

  • 把它切成若干段
  • 每一段都必须是回文串
  • 返回所有可能的切分方案。

例子:

s = "aab"
输出:[["a", "a", "b"], ["aa", "b"]]

核心思路

从左往右切字符串,只要当前切出来的子串是回文,就继续往后切。

  1. 从下标 start 开始切
  2. 枚举所有可能的结束位置 end
  3. 如果 s[start:end] 是回文
    • 把它加入当前方案
    • 从 end 继续切
  4. 当切到字符串末尾
    • 当前方案就是一个合法答案

代码实现

from typing import List

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        res = []
        path = []

        def backtracking(start: int):
            # 如果已经切到字符串末尾
            if start == len(s):
                res.append(path[:])
                return
            # 枚举所有可能的切割位置
            for end in range(start + 1, len(s) + 1):
                substring = s[start:end]
                # 只有当 substring 是回文,才继续切
                if substring == substring[::-1]:
                    path.append(substring)
                    backtracking(end)
                    path.pop()

        backtracking(0)
        return res
指标复杂度说明
时间复杂度(上界)O(n · 2ⁿ)最坏情况下(如全是同一字符),需要枚举所有切分方式
空间复杂度(不含输出)O(n)递归深度 + path 最多切 n 次
输出空间O(n · 2ⁿ)需要存所有切分方案

目录

  1. 39. 组合总和
  2. 核心思路与剪枝
  3. 代码实现
  4. 40. 组合总和 II
  5. 核心思路
  6. 去重的关键规则
  7. 代码实现
  8. 131. 分割回文串
  9. 核心思路
  10. 代码实现
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • PowerShell 中 Invoke-WebRequest 的正确使用:避免参数匹配错误
  • Microsoft 365 Copilot 与 Copilot Chat 详细对比
  • Harness Engineering:AI Agent 时代的新工程范式
  • 无人机飞行空域申请全流程指南
  • CKS 核心命令速查指南
  • Whisper.cpp 本地语音识别实现与配置指南
  • Stable Diffusion 本地模型 base_model 路径配置与加载指南
  • 2024 大模型从业者的至暗时刻
  • 多端多接口多向传导空战数据链体系:异构融合与抗毁弹性网络设计
  • Jenkins+docker容器部署前端Vue项目详细教程
  • Skills 市场与共享经济实战指南:从 Web 到 AI 架构
  • FreeCAD 修复 STL 网格并转换为实体模型指南
  • C++进阶:从裸指针到智能指针的内存管理进化
  • VS Code 无法下载 .vsix 插件的离线安装方案(以 C/C++ 插件为例)
  • Python 爬虫实战:爬取网易云与 QQ 音乐歌曲信息
  • Android Jetpack 架构组件详解与实战指南
  • Windows 系统下载、安装并运行 MinIO 服务及访问 WebUI
  • Java 充电桩平台故障排查优化:日志分级存储与链路关联方案
  • 西门子 S7-1200FC PLC 与松下机器人 Profinet 通信及外部自动控制
  • Python pandas 数据分析入门与实战

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online