当前位置: 首页 > news >正文

39. 组合总和

这题是回溯里的经典题。

它和:

  • 全排列

  • 子集

  • 括号生成

最大的不同是:

元素可以重复使用

这是核心。


一、这题本质是什么?

比如:

candidates = [2,3,5] target = 8

你需要:

不断尝试选数字 直到总和 == 8

例如:

2 -> 2 -> 2 -> 2

或者:

2 -> 3 -> 3

二、回溯模板怎么想?

回溯其实就一句话:

尝试 -> 递归 -> 撤销选择

三、决策树(真正理解)

以:

[2,3,6,7] target = 7

为例。

从空开始:

[]

第一层:

选2 选3 选6 选7

如果先选2:

[2] 剩余 target = 5

继续:

[2,2] 剩余3

继续:

[2,2,2] 剩余1

已经不可能:

因为最小是2。

回溯。


然后:

[2,2,3] 剩余0

找到答案。


四、为什么不会出现重复组合?

比如:

[2,2,3] 和 [2,3,2]

其实是同一种。


怎么避免?

用 start 参数

意思是:

后面只能从当前及以后开始选

例如:

当你已经选了2:

下一层还能选:

2 3 6 7

但如果已经选了3:

下一层只能选:

3 6 7

不能再回头选2。

这样天然去重。


五、为什么这里递归传 i 而不是 i+1?

这是整题关键。


因为元素可以重复使用

例如:

2 -> 2 -> 2

所以:

dfs(i)

表示:

当前数字还能继续选

如果写:

dfs(i + 1)

就变成:

一个数字只能用一次

那就是另一题:

组合总和 II


六、完整代码(重点理解)

class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> path = new ArrayList<>(); public List<List<Integer>> combinationSum(int[] candidates, int target) { dfs(candidates, target, 0); return res; } private void dfs(int[] candidates, int target, int start) { // 找到答案 if (target == 0) { res.add(new ArrayList<>(path)); return; } // 剪枝 if (target < 0) { return; } for (int i = start; i < candidates.length; i++) { // 选择 path.add(candidates[i]); // 递归 dfs(candidates, target - candidates[i], i); // 回溯 path.remove(path.size() - 1); } } }

七、执行流程(必须看懂)

例如:

2 3 6 7 target=7

开始:

path=[] target=7

选2:

path=[2] target=5

再选2:

path=[2,2] target=3

再选2:

path=[2,2,2] target=1

再选2:

target=-1

结束。

回溯:

path=[2,2]

然后尝试3:

path=[2,2,3] target=0

加入答案。


八、这题和子集有什么区别?

子集

每个元素:

选 / 不选

组合总和

每个元素:

可以选无限次

所以:

dfs(i)

而不是:

dfs(i+1)

九、回溯题统一模板

你会发现:

路径 path 结果 res for循环枚举选择 递归 撤销选择

几乎所有回溯题都这样。

只是变化:

题目不同点
全排列用used数组
子集每层直接加入答案
组合总和dfs(i)可重复使用
括号生成有左右括号限制

本质都是:

在决策树上DFS
http://www.jsqmd.com/news/799523/

相关文章:

  • 智能音箱隐私安全深度解析:从唤醒词到数据流,如何与AI助手安全共处
  • LitGPT:从零实现LLM,打造透明可控的大模型全流程工具箱
  • 开源记忆系统mem0:AI智能体与知识管理的向量化核心引擎
  • OpenAI API 协议学习
  • GPU内核优化技术:R3框架原理与实践
  • FPGA/CPLD数字系统设计实战:从器件选型到调试验证的工程指南
  • 如何快速搭建微信机器人:WeixinBot完整使用指南
  • 汽车LED热管理:原理、测量与CFD仿真实践
  • GitOps工作流模式:自动化基础设施和应用部署
  • 模块化IC设计流程:应对复杂芯片挑战的解决方案
  • 优化ESP32 ADF 音频问题
  • Arm嵌入式C/C++库架构与Semihosting机制解析
  • 5分钟快速上手:如何用Video2X免费AI工具让老旧视频焕发4K新生
  • 为什么92%的数据分析师还没用上Gemini Sheets功能?—— 一份被谷歌官方忽略的AI分析落地清单
  • NVIDIA aicr:AI容器运行时核心原理与生产部署指南
  • 蓝牙技术演进与物联网应用全解析
  • [具身智能-678]:ROS2 功能包 = 动态库 + 可执行节点 + launch 文件 三合一!
  • 从样式覆盖到版本升级:全面解析Antd表格固定列对齐问题的解决路径
  • 告别一堆转换头!一个自研小工具搞定USB、网口、485、232、TTL全互连(附配置软件)
  • ARM GIC中断控制器PPI配置与优先级设置详解
  • Fate/Grand Automata终极指南:如何用Android自动化脚本告别FGO枯燥刷本
  • 基于vue和微信小程序的校园自助打印系统(30293)
  • 可穿戴设备十年演进:从腕上手机到身体局域网与场景化体验
  • A64指令集LDAPURSH与LDAR内存访问机制解析
  • DES算法C++实现踩坑实录:S盒置换与比特操作的那些坑
  • 科技产业投资困局:国家安全与就业增长如何平衡?
  • Hack The Box注册失败?别慌,可能是你的‘上网姿势’不对(附最新可用方案)
  • 从建模到Debug:手把手用UPPAAL复现一个经典互斥算法,并学会分析反例
  • 嵌入式硬件设计实战:从芯片选型到系统稳定性的工程指南
  • 光纤偏振测量:从琼斯矢量到庞加莱球,六种工具深度解析与工程实践