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

今日算法(组合问题III)(回溯的使用)

题目描述找出所有相加之和为n的k个数的组合且满足下列条件只使用数字 1 到 9每个数字最多使用一次返回所有可能的有效组合的列表列表不能包含相同的组合两次组合可以以任何顺序返回核心思路带双重剪枝的回溯法这道题是LeetCode 77. 组合的进阶版本质还是组合枚举问题只是在 选 k 个数 的基础上增加了 和为 n 的约束条件。回溯三要素路径用path保存当前正在构建的组合sum保存当前组合的和选择列表从start位置开始选择数字保证组合递增避免重复终止条件当path.size() k且sum n找到有效组合加入结果集当path.size() k但sum ! n无效组合直接返回双重剪枝优化必加大幅提升效率和剪枝如果当前和sum i n后面的数字都比i大加起来肯定超过n直接终止循环数量剪枝剩余需要选need k - path.size()个数字因此i最大只能到9 - need 1否则后面不够选need个数字完整代码实现Ccppclass Solution { public: vectorvectorint combinationSum3(int k, int n) { vectorvectorint res; // 存储所有结果 vectorint path; // 存储当前正在构建的组合 backtrack(k, n, 1, 0, path, res); return res; } private: // start: 本轮可以选择的起始位置 // sum: 当前组合的和 void backtrack(int k, int n, int start, int sum, vectorint path, vectorvectorint res) { // 1. 递归终止条件选够了k个数 if (path.size() k) { if (sum n) { res.push_back(path); // 和为n加入结果集 } return; } // 2. 双重剪枝优化 // 剪枝1数量剪枝剩余数字不够选need个 int need k - path.size(); // 剪枝2和剪枝当前和i超过n后面更大的数字不用看了 for (int i start; i 9 - need 1 sum i n; i) { // 3. 选择当前数字 path.push_back(i); // 4. 递归下一轮从i1开始选择避免重复 backtrack(k, n, i 1, sum i, path, res); // 5. 回溯撤销选择尝试下一个数字 path.pop_back(); } } };详细执行流程解析以示例 2k3, n9为例模拟回溯过程初始状态res []path []start 1sum 0递归过程第一层递归start1path[]sum0need 3循环i从1到79-317i1path [1]sum 1进入第二层递归start2第二层need 2循环i从2到8i2path [1,2]sum 3进入第三层递归start3第三层need 1循环i从3到9i3sum36 9path[1,2,3]长度 3和 6≠9返回i4sum47 9path[1,2,4]和 7≠9返回i5sum58 9path[1,2,5]和 8≠9返回i6sum69 9path[1,2,6]加入res返回回溯path[1,2]i3path [1,3]sum 4进入第三层递归start4i5sum59 9path[1,3,5]加入resi4path [1,4]sum 5进入第三层递归start5所有i加起来都超过 9无结果回溯path[1]i2path [2]sum 2进入第二层递归start3i3path [2,3]sum 5进入第三层递归start4i4sum49 9path[2,3,4]加入res后续i均无有效组合最终结果res [[1,2,6],[1,3,5],[2,3,4]]与示例输出完全一致。关键细节与易错点start参数的作用每次递归从start位置开始选择保证组合中的元素是递增的避免出现[2,1,3]这种重复组合。双重剪枝的必要性数量剪枝避免无效的循环例如当还需要选 3 个数字时i最大只能到 7因为 8 和 9 只能选 2 个不够 3 个和剪枝提前终止不可能满足和条件的分支大幅减少递归次数sum的传递方式这里用值传递sum每次递归传递sum i不需要在回溯时手动减i代码更简洁如果用引用传递需要在path.pop_back()后加上sum - i。与 LeetCode 77. 组合的对比77 题只需要选 k 个数没有和的约束216 题在 77 题的基础上增加了和为 n 的约束以及对应的和剪枝复杂度分析时间复杂度\(O(C(9, k) \times k)\)最多有 \(C(9, k)\) 个有效组合每个组合需要复制一次到结果集时间复杂度为 \(O(k)\)空间复杂度\(O(k)\)递归调用栈的深度等于k临时路径path的最大长度也为k不包含结果集的额外空间。总结组合总和 III 是组合问题的经典进阶题核心解法是带双重剪枝的回溯法。通过start参数保证组合不重复通过数量剪枝和和剪枝大幅优化效率。这道题的思路可以推广到所有组合枚举类问题是掌握回溯法的必练题目。迭代法核心思想递归回溯的本质是深度优先搜索DFS而 DFS 可以用栈来模拟实现。我们把递归过程中每一层的状态起始位置、当前和、当前路径都保存在栈中通过不断弹出栈顶元素、压入新状态的方式模拟递归的入栈和出栈过程。栈中需要保存的状态start本轮可以选择的起始数字保证组合递增避免重复sum当前组合的和path当前正在构建的组合迭代法的剪枝优化和递归法完全一致我们同样可以加入双重剪枝大幅减少无效的状态入栈数量剪枝剩余需要选need k - path.size() - 1个数字因此i最大只能到9 - need 1和剪枝如果sum i n后面的数字都比i大加起来肯定超过n直接终止循环完整迭代法代码实现Ccppclass Solution { public: vectorvectorint combinationSum3(int k, int n) { vectorvectorint res; // 栈中保存(起始位置start, 当前和sum, 当前路径path) stacktupleint, int, vectorint stk; // 初始状态从1开始选和为0路径为空 stk.emplace(1, 0, vectorint()); while (!stk.empty()) { // 弹出栈顶元素模拟递归返回 auto [start, sum, path] stk.top(); stk.pop(); // 1. 终止条件选够了k个数 if (path.size() k) { if (sum n) { res.push_back(path); // 和为n加入结果集 } continue; } // 2. 双重剪枝优化 int need k - path.size(); // 还需要选need个数字 // 遍历所有可能的选择 for (int i start; i 9 - need 1 sum i n; i) { // 3. 生成新状态压入栈模拟递归调用 vectorint new_path path; new_path.push_back(i); stk.emplace(i 1, sum i, new_path); } } return res; } };代码逐行解析栈的初始化cppstacktupleint, int, vectorint stk; stk.emplace(1, 0, vectorint());我们用一个三元组栈保存每一层的状态初始时压入最外层的状态从数字 1 开始选当前和为 0路径为空。主循环模拟递归过程cppwhile (!stk.empty()) { auto [start, sum, path] stk.top(); stk.pop(); // ... }只要栈不为空就不断弹出栈顶元素进行处理这对应递归过程中函数的返回。终止条件处理cppif (path.size() k) { if (sum n) { res.push_back(path); } continue; }和递归法完全一致当选够 k 个数时检查和是否为 n是的话加入结果集然后直接跳过后续处理。状态生成与压栈cppfor (int i start; i 9 - need 1 sum i n; i) { vectorint new_path path; new_path.push_back(i); stk.emplace(i 1, sum i, new_path); }这对应递归过程中的选择和递归调用生成新的路径new_path加入当前选择的数字i生成新的状态下一轮从i1开始选和为sumi路径为new_path将新状态压入栈等待后续处理详细执行流程解析我们还是以示例 2k3, n9为例模拟迭代法的执行过程初始状态栈[(1, 0, [])]结果集[]第一步弹出初始状态弹出(1, 0, [])path.size()0 3need3循环i从 1 到 79-317压入新状态(2,1,[1])、(3,2,[2])、(4,3,[3])、(5,4,[4])、(6,5,[5])、(7,6,[6])、(8,7,[7])栈现在[(2,1,[1]), (3,2,[2]), (4,3,[3]), (5,4,[4]), (6,5,[5]), (7,6,[6]), (8,7,[7])]第二步弹出栈顶(8,7,[7])path.size()1 3need2循环i从 8 到9-218sumi7815 9不满足和剪枝循环不执行栈现在[(2,1,[1]), (3,2,[2]), (4,3,[3]), (5,4,[4]), (6,5,[5]), (7,6,[6])]第三步弹出栈顶(7,6,[6])path.size()1 3need2循环i从 7 到 8i7sumi6713 9不满足循环不执行栈现在[(2,1,[1]), (3,2,[2]), (4,3,[3]), (5,4,[4]), (6,5,[5])]第四步弹出栈顶(6,5,[5])path.size()1 3need2循环i从 6 到 8i6sumi5611 9不满足循环不执行栈现在[(2,1,[1]), (3,2,[2]), (4,3,[3]), (5,4,[4])]第五步弹出栈顶(5,4,[4])path.size()1 3need2循环i从 5 到 8i5sumi459压入(6,9,[4,5])i6sumi4610 9终止循环栈现在[(2,1,[1]), (3,2,[2]), (4,3,[3]), (6,9,[4,5])]第六步弹出栈顶(6,9,[4,5])path.size()2 3need1循环i从 6 到 9sumi9615 9不满足循环不执行栈现在[(2,1,[1]), (3,2,[2]), (4,3,[3])]第七步弹出栈顶(4,3,[3])path.size()1 3need2循环i从 4 到 8i4sumi347压入(5,7,[3,4])i5sumi358压入(6,8,[3,5])i6sumi369压入(7,9,[3,6])i7sumi3710 9终止循环栈现在[(2,1,[1]), (3,2,[2]), (5,7,[3,4]), (6,8,[3,5]), (7,9,[3,6])]后续过程继续按照这个逻辑弹出和压入状态最终会依次找到[2,3,4]、[1,3,5]、[1,2,6]三个有效组合和示例输出完全一致。递归法 vs 迭代法对比表格对比维度递归回溯法迭代法代码简洁度非常简洁逻辑清晰稍复杂需要手动管理栈理解难度容易理解符合人类思维需要理解递归的栈模拟过程栈溢出风险递归深度最大为 9无风险无栈溢出风险效率几乎一致都有双重剪枝几乎一致适用场景大多数回溯问题递归深度过大的场景或不允许使用递归的场景总结迭代法实现回溯的核心是用栈模拟递归的状态转移。对于这道题来说递归法已经足够优秀但掌握迭代法可以让我们更深入地理解回溯的本质也能应对一些特殊场景如递归深度限制。

相关文章:

今日算法(组合问题III)(回溯的使用)

题目描述找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:只使用数字 1 到 9每个数字 最多使用一次返回所有可能的有效组合的列表,列表不能包含相同的组合两次,组合可以以任何顺序返回核心思路:带双重剪枝的回溯…...

2026保姆级免费照片去水印教程:不用下载App,微信小程序3步搞定!

你是不是也遇到过这种崩溃瞬间?刷到一张绝美壁纸想存下来当背景,结果水印刚好挡住主角的脸;看到一段搞笑视频想转发给朋友,结果水印横在中间像个挡箭牌;想拿一张素材做作业PPT,结果水印比内容还显眼。更烦的…...

2026最新免费在线去水印工具详细教程,在线去本地视频水印保姆级指南

你是不是也遇到过这种情况?辛辛苦苦在网上找到一个绝美视频素材想用在剪辑里,结果画面正中央横着一个硕大的水印;或者刷小红书看到一段干货满满的教学视频,想保存下来反复学习,却被角落的Logo劝退。更头疼的是&#xf…...

2026最新免费在线去除视频水印保姆级教程,不用下载软件一步到位!

你是不是也遇到过这种崩溃瞬间:刷到一个绝美空镜想拿来做转场,结果角落挂着硕大的平台台标;翻到一条神评论视频想分享给朋友,水印叠水印糊成一片;好不容易找到素材想剪辑个二创,却被满屏的浮动水印直接劝退…...

2026照片去水印免费软件App推荐,详细教程一看就会

你是不是也遇到过这种情况?刷到一张特别喜欢的照片想保存当壁纸,结果右下角一个巨大的水印直接毁了整张图;或者做PPT需要用到某张素材图,翻遍了相册发现都有平台Logo,怎么裁都裁不掉。想找免费的去水印工具&#xff0c…...

2026保姆级教程:免费一键去图片水印的App有哪些?这几种方法一看就会

你是不是也遇到过这种抓狂的时刻?好不容易在网上找到一张绝美壁纸或实用素材,保存下来一看,角落那个水印直接毁掉了整张图的氛围。更气人的是,你尝试用相册自带的编辑功能去涂抹,结果越涂越糊,最后只能无奈…...

K210开发板固件烧录:使用kflash_gui图形化工具的完整指南

K210开发板固件烧录:使用kflash_gui图形化工具的完整指南 【免费下载链接】kflash_gui Cross platform GUI wrapper for kflash.py (download(/burn) tool for k210) 项目地址: https://gitcode.com/gh_mirrors/kf/kflash_gui 在K210开发板生态系统中&#x…...

云原生事件驱动架构:构建高效的事件处理系统

云原生事件驱动架构:构建高效的事件处理系统 引言 在云原生环境中,事件驱动架构是一种高效的系统设计模式。通过事件驱动,可以实现松耦合、高可用的系统。事件驱动架构已经成为构建现代化应用的重要方法。 作为一名资深的DevOps工程师&#x…...

技术人的沟通技巧:如何与非技术人员有效沟通

技术人的沟通技巧:如何与非技术人员有效沟通 引言 作为一名技术人,我们不仅需要具备扎实的技术能力,还需要具备良好的沟通能力。特别是当我们需要与非技术人员沟通时,如何将复杂的技术问题用简单易懂的语言表达出来,是…...

技术人的职业规划:打造成功的职业生涯

技术人的职业规划:打造成功的职业生涯 引言 作为一名技术人,职业规划是实现职业目标的关键。在快速变化的技术领域,一个清晰的职业规划可以帮助我们明确方向,抓住机会,实现个人价值。 回顾我的职业历程,从一…...

哈夫曼树:高效压缩数据的秘密武器

引言在前面的树系列中,我们学习了二叉搜索树、AVL 树和红黑树——它们都是为了高效查找而设计的。今天要讲的哈夫曼树,目的完全不同:它是为了压缩数据而生。哈夫曼树(Huffman Tree),又称最优二叉树&#xf…...

数字孪生AI流水线设计:Function+Data Flow框架解析与实践

1. 项目概述:当数字孪生遇上机器学习流水线如果你正在构建一个数字孪生系统,无论是为了预测一座桥梁的疲劳寿命,还是模拟一台精密电机的电磁行为,你大概率会用到机器学习。这听起来很酷,但实际操作起来,往往…...

量子机器学习在网络安全领域的算法演进与实践挑战

1. 量子机器学习:当算力革命遇见智能算法如果你关注过近几年的科技新闻,一定对“量子计算”这个词不陌生。它常常与“颠覆”、“革命”这样的词汇一同出现,听起来既神秘又遥远。但作为一名长期混迹在网络安全和算法优化一线的从业者&#xff…...

DeepSeek模型版本选择终极决策树(2024Q3权威更新):输入你的GPU型号/任务类型/预算,3步锁定最优解

更多请点击: https://codechina.net 第一章:DeepSeek模型版本选择终极决策树(2024Q3权威更新):输入你的GPU型号/任务类型/预算,3步锁定最优解 选择适配的 DeepSeek 模型版本是高效落地大模型应用的关键前提…...

Gemini LTV建模实战手册:从POC验证、规模化推理、监管审计到知识沉淀——覆盖7大关键节点的稀缺性价值锚定法

更多请点击: https://codechina.net 第一章:Gemini生命周期价值分析 Gemini模型的生命周期价值(Lifetime Value, LTV)并非仅由初始部署成本或单次推理费用决定,而是贯穿于模型选型、集成、运行、监控、迭代与退役的全…...

蛋白质设计新范式:QUBO建模与迭代学习框架解析

1. 项目概述与核心思路在生物信息学和计算生物学领域,蛋白质设计一直是一个“圣杯”级别的挑战。简单来说,它要回答一个逆向问题:给定一个我们想要的蛋白质三维结构,如何从头设计出能折叠成这个结构的氨基酸序列?传统方…...

为什么你的Gemini总生成错误JOIN?深度拆解语义理解断层、外键缺失与上下文截断三大黑洞

更多请点击: https://intelliparadigm.com 第一章:为什么你的Gemini总生成错误JOIN?深度拆解语义理解断层、外键缺失与上下文截断三大黑洞 当Gemini面对多表SQL生成任务时,频繁输出逻辑错误的JOIN语句——例如对无关联字段的表强…...

机器学习原子间势与连续介质模型在柔性InSe扭转双层原子重构研究中的应用

1. 项目概述:当柔性二维材料遇上扭转角在二维材料的世界里,一个简单的“扭转”操作,往往能打开一扇通往新奇物理现象的大门。从魔角石墨烯中发现的超导和关联绝缘态,到过渡金属硫族化合物(TMDs)中的莫尔激子…...

Wireshark抓不到国密TLCP流量?揭秘协议解析断层与电信数智版实战方案

1. 为什么普通Wireshark根本抓不到国密流量——从协议栈底层看TLCP的“隐身”逻辑你有没有试过,在一台刚装好国密SM2/SM3/SM4算法支持的Linux服务器上,用标准Wireshark 3.7.1抓包,结果在过滤器里输入tls或ssl,却一条加密握手记录都…...

对抗机器学习攻击范式解析:后门、对抗样本与权重攻击的攻防全景

1. 对抗机器学习攻击范式全景解析在AI模型日益渗透到关键决策领域的今天,其安全性问题已经从学术探讨演变为迫在眉睫的现实挑战。作为一名长期关注模型安全的研究者和实践者,我见过太多“表现优异”的模型在精心设计的微小扰动面前瞬间“失智”。对抗机器…...

深度学习篇---cuSPARSELt

cuSPARSELt 是 NVIDIA CUDA 生态中一个专门为结构化稀疏矩阵设计的 GPU 加速数学库。它和我们常说的 cuSPARSE 是同门师兄弟,但各有绝活。如果说 cuSPARSE 是什么都能处理的“通用军刀”,那 cuSPARSELt 就是为深度学习这类特定任务量身定制的“手术刀”。…...

iOS抓包防护绕过:合规调试的三层穿透实践

1. 这不是“破解”,而是开发者本该掌握的合规调试能力很多人看到“iOS抓包防护绕过”第一反应是:这不就是搞逆向、破壳、绕过安全检测?甚至下意识联想到灰色工具链或越狱环境。但我要先说清楚——本文所有操作,均在苹果官方允许的…...

深度学习篇---NVIDIA DeepStream

NVIDIA DeepStream 是一个功能强大的流媒体分析工具包,专为基于 AI 的多传感器处理、视频、音频和图像理解而设计。你可以把它想象成一个“视觉 AI 应用的乐高工厂”,它把视频解码、AI 推理、目标追踪这些复杂的“零件”,巧妙地组合成一条高效…...

鸿蒙健身计划页面构建:动作清单与训练部位分布模块详解

鸿蒙健身计划页面构建:动作清单与训练部位分布模块详解 前言 在 HarmonyOS 6.0 应用开发中,健身类页面的训练动作展示和训练部位分析是用户执行训练计划的核心参考模块。本文将以“健身计划”应用中的“动作清单”垂直列表模块和“训练部位分布”进度条网…...

torchvision transforms 报错怎么办?教你一招避坑

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 torchvision.transforms报错大揭秘:一招解决90%的坑目录torchvision.transforms报错大揭秘:一招解决90%的…...

鸿蒙健身计划页面构建:训练英雄区与今日训练模块详解

鸿蒙健身计划页面构建:训练英雄区与今日训练模块详解 前言 在 HarmonyOS 6.0 应用开发中,健身类页面的核心挑战在于如何展示训练进度、训练目标和实时数据。本文将以“健身计划”应用的主页面为例,深入解析如何在鸿蒙平台上构建健身管理类应用…...

你的GPU内存还好吗?MemTestCL深度诊断指南

你的GPU内存还好吗?MemTestCL深度诊断指南 【免费下载链接】memtestCL OpenCL memory tester for GPUs 项目地址: https://gitcode.com/gh_mirrors/me/memtestCL 你的显卡在运行大型游戏时会不会突然花屏?AI训练过程中是否经常遇到莫名其妙的崩溃…...

Legacy iOS Kit深度拆解:揭秘旧款iOS设备重生的技术魔法

Legacy iOS Kit深度拆解:揭秘旧款iOS设备重生的技术魔法 【免费下载链接】Legacy-iOS-Kit An all-in-one tool to restore/downgrade, save SHSH blobs, jailbreak legacy iOS devices, and more 项目地址: https://gitcode.com/gh_mirrors/le/Legacy-iOS-Kit …...

对比自建代理,使用Taotoken聚合平台在稳定性与运维上的体验提升

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 对比自建代理,使用Taotoken聚合平台在稳定性与运维上的体验提升 过去,一些开发团队为了便捷地使用特定的大…...

Nginx基于反向代理的负载均衡

一、引言:从单点到集群,流量分发的艺术当你的应用用户量从几百飙升到几万,单台服务器很快就会成为性能瓶颈,甚至面临宕机风险。此时,最直接有效的解决方案就是横向扩展——部署多台服务器组成集群。但新问题随之而来&a…...