news 2026/5/11 2:17:33

回溯算法--组合总和II

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
回溯算法--组合总和II

问题要求:给定一个候选数集 (candidates) 和一个目标数 (target),找出 candidates 中所有可以使数字和为 target 的组合。
关键约束:
1. candidates 中的每个数字在每个组合中只能使用一次。
2. 解集不能包含重复的组合。

一句话就是:有重复的元素怎么得到不重复的集合

比如{1,1,1, 2},target = 3, 问题的难点在于,最终的集合是{1, 2},{1, 1, 1}而在以往的算法答案是{1, 2}, {1, 2},{1, 2}为什么有三个{1, 2},是因为有三个{1, 1, 1}, 因此难点在于怎么去重。

分析

首先我们已知回溯算法可以抽象为一颗树,分为横向和纵向,横向表示同一层的元素,比如{1, 2}, {1, 2},{1, 2},这是因为三个1都在第一层,分别选择了三次,纵向表示同一树枝的元素,比如{1, 1, 1}表示三个1分别在三层,他们在同一个集合,三次递归选中了1。

通过以上的分析可知,我们想要得到不重复的集合就是要在去重同一层重复的元素。

思路

首先肯定是要对元素进行排序,一方面是为了剪枝,一方面方便处理重复的元素。

去重逻辑 (关键)
当满足以下三个条件时,跳过当前元素以避免重复组合:
1. i > 0:确保 i-1 索引有效。
2. candidates[i] == candidates[i - 1]:当前元素与前一个元素相同。
3. used[i - 1] == false:前一个相同的元素在搜索中没有被使用,这表明二者是同一层的元素,如果used[i - 1] == true,表明二者是同一个树枝上的元素,所以这个条件十分重要,用来确定是同一层,还是同一树枝。
这表示我们已经处理过以 candidates[i-1] 开头的组合,现在为了避免重复,需要跳过以 candidates[i] 开头的相同组合。

if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) { continue; }

代码

#include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; // LeetCode 问题:组合总和 II // 问题要求:给定一个候选数集 (candidates) 和一个目标数 (target),找出 candidates 中所有可以使数字和为 target 的组合。 // 关键约束: // 1. candidates 中的每个数字在每个组合中只能使用一次。 // 2. 解集不能包含重复的组合。 class Solution { private: vector<int> path; // 存储当前正在构建的组合路径 vector<vector<int>> result; // 存储所有符合条件的最终组合 /** * @brief 核心回溯函数 * @param candidates 候选数组 * @param targetSum 目标和 * @param sum 当前路径的和 * @param startIndex 搜索的起始索引,用于防止元素重复使用 * @param used 标记数组,用于在处理重复数时去重 */ void backtracking(const vector<int>& candidates, int targetSum, int sum, int startIndex, vector<bool>& used) { // 终止条件:如果当前和等于目标值,说明找到了一个有效组合 if (sum == targetSum) { result.push_back(path); // 将当前路径添加到结果集中 return; } // 遍历候选数组 // 剪枝优化:i < candidates.size() && sum + candidates[i] <= targetSum // 如果当前元素加上当前和已经超过了目标值,那么后续更大的元素也必然超过,因此可以提前终止循环。 for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= targetSum; i++) { // 去重逻辑 (关键) // 当满足以下三个条件时,跳过当前元素以避免重复组合: // 1. i > 0:确保 i-1 索引有效。 // 2. candidates[i] == candidates[i - 1]:当前元素与前一个元素相同。 // 3. used[i - 1] == false:前一个相同的元素在搜索中没有被使用,这表明二者是同一层的元素,如果used[i - 1] == true,表明二者是同一个树枝上的元素 // 这表示我们已经处理过以 candidates[i-1] 开头的组合,现在为了避免重复, // 需要跳过以 candidates[i] 开头的相同组合。 if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) { continue; } // --- 做出选择 --- path.push_back(candidates[i]); // 将当前元素加入路径 used[i] = true; // 标记当前元素已使用 sum += candidates[i]; // 更新当前和 // --- 递归进入下一层 --- // 递归调用 backtracking,从下一个索引 i + 1 开始搜索,确保每个数字只用一次。 backtracking(candidates, targetSum, sum, i + 1, used); // --- 撤销选择 (回溯) --- sum -= candidates[i]; // 恢复当前和 used[i] = false; // 取消标记 path.pop_back(); // 将当前元素移出路径,为同层其他选择让路 } } public: /** * @brief 主函数,用于寻找所有和为 target 的组合 * @param candidates 候选数组 * @param target 目标和 * @return 返回所有符合条件的组合 */ vector<vector<int>> combinationSum2(vector<int>& candidates, int target) { path.clear(); // 清空上一次的路径 result.clear(); // 清空上一次的结果 // 初始化 used 数组,大小与 candidates 相同,全部置为 false (未使用) vector<bool> used(candidates.size(), false); // 排序是去重逻辑的前提。排序后,所有相同的元素都会相邻排列。 sort(candidates.begin(), candidates.end()); // 从索引 0 开始进行回溯搜索 backtracking(candidates, target, 0, 0, used); return result; // 返回最终结果 } }; // --- 主测试函数 --- int main() { Solution S; // 创建 Solution 类的实例 // 测试用例:一个包含重复数字的数组 vector<int> candidates = {10, 1, 2, 7, 6, 1, 5, 2, 2, 2}; int target = 8; // 调用函数获取所有符合条件的组合 vector<vector<int>> res = S.combinationSum2(candidates, target); // --- 输出结果 --- cout << "所有和为 " << target << " 的组合:" << endl; for (auto& row : res) { // 遍历每一个组合 (row) for (auto& col : row) { // 遍历组合中的每一个数字 (col) cout << col << " "; } cout << endl; // 每个组合输出后换行 } return 0; // 程序正常退出 }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 20:48:43

comfyui + fluxGym角色固定工作流实战

FluxGym是什么 FluxGym 是一个专为 FLUX 模型设计的、极简化的 LoRA 训练工具。它的核心目的是让普通用户在消费级显卡&#xff08;如 12GB/16GB 显存&#xff09;上也能轻松LoRA&#xff0c;训练 AI 模型&#xff0c;无需面对复杂的参数设置&#xff0c;如果你想给 FLUX 炼制一…

作者头像 李华
网站建设 2026/5/9 21:53:03

特殊版解密神器,无限制,真好用!

APDFPR PDF解密软件 解压后&#xff0c;无需繁琐的安装步骤&#xff0c;直接点击对应图标即可打开使用。 首次使用时&#xff0c;建议先将软件界面设置为中文&#xff0c;这样操作起来会更加得心应手。 为了让大家更直观地感受它的强大功能&#xff0c;我们来做个小演示…

作者头像 李华
网站建设 2026/5/9 14:06:34

【专科生必看】查重率90%?AI痕迹99.8%?别慌!Paperzz三招教你3元搞定降重+降AIGC,导师都说“这孩子真会用工具”!

Paperzz-AI官网免费论文查重复率AIGC检测/开题报告/文献综述/论文初稿 https://www.paperzz.cc/weighthttps://www.paperzz.cc/weight 副标题&#xff1a; 专科论文不用熬通宵&#xff01;只需上传文档→选“智能降重”或“降AIGC”→等10分钟&#xff0c;重复率从90%降到8%&am…

作者头像 李华
网站建设 2026/5/10 17:56:23

DBO-LSTM预测模型:含注释、易替换数据的优化时间序列预测模型

DBO-LSTM预测模型&#xff0c;DBO优化LSTM的时间序列预测模型&#xff0c;有注释&#xff0c;替换数据就可以运行&#xff0c;全部自己写的&#xff0c;注释为中文&#xff0c;方便修改&#xff0c;有与基础版LSTM的对比结果图与误差对比图。 很适合同学们学习与绘图 最近在研…

作者头像 李华
网站建设 2026/5/9 10:03:24

小型无人机轻量化,提升续航的几种方法

小型无人机减重并提升续航能力&#xff0c;需要从机身结构、核心部件、动力系统、负载配置、飞行策略五个核心维度系统性优化&#xff0c;结合激光雷达&#xff08;Mid360&#xff09;减重改造技术&#xff0c;还可以针对性匹配无人机的负载轻量化需求&#xff0c;具体方案如下…

作者头像 李华
网站建设 2026/5/10 19:37:04

【LLM学习】九、MCP深度解析

本期对MCP进行深入解析&#xff0c;MCP的最小应用回顾往期内容&#xff1a; 【LLM学习】【Ollama】四、MCP【LLM学习】【Ollama】五、MCP进阶 一、MCP 是什么&#xff1f;—— 从行业痛点看协议价值​ 在深入技术细节前&#xff0c;我们先明确 MCP 的核心定位&#xff1a;MC…

作者头像 李华