回溯算法(C/C++)
目录
一、组合问题
组合
组合剪枝
组合总和 III编辑
组合总和编辑
组合总和 II
电话号码的字母组合编辑
二、分割问题
分割回文串
复原 IP 地址
三、集合问题
子集
子集 II
非递减子序列
四、排列问题
全排列
全排列 II
五、棋盘问题
N 皇后
课程:
【带你学透回溯算法(理论篇)| 回溯法精讲!】https://www.bilibili.com/video/BV1cy4y167mM?vd_source=342079de7c07f82982956aad8662b467
关于回溯算法,你该了解这些!
- 组合问题:N个数里面按一定规则找出k个数的集合
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 棋盘问题:N皇后,解数独等等
void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果}
}
一、组合问题
组合
class Solution {
public:void backtracking(int pos, vector<int> &path, vector<vector<int>>& res, int k, int n) {if (path.size()==k) {res.push_back(path);return;}for (int i = pos;i < n+1;i++) {path.push_back(i);backtracking(i + 1, path, res, k, n);path.pop_back();}}vector<vector<int>> combine(int n, int k) {vector<vector<int>> res;vector<int> path;backtracking(1, path, res, k, n);return res;}
};
组合剪枝
在上面的这个组合的代码,实际上我们可以进一步去优化,也就是对构造的树进行剪枝的操作。我们可以这么理解,已知可以选择走的路径总长度为n,在当前的横向遍历节点上,已知前面走过路径上长度为size,然后总共是需要走的长度为k,那么是不是可以说还需要走k-size的长度的路径?是吧,那也意味着在当前横向遍历的节点长度最起码是从n-(k-size)+1开始,这个加一是表示字节当前这个节点。

修改的代码如下:
class Solution {
public:void backtracking(int pos, vector<int> &path, vector<vector<int>>& res, int k, int n) {if (path.size()==k) {res.push_back(path);return;}for (int i = pos;i <= n - (k - path.size()) + 1;i++) {path.push_back(i);backtracking(i + 1, path, res, k, n);path.pop_back();}}vector<vector<int>> combine(int n, int k) {vector<vector<int>> res;vector<int> path;backtracking(1, path, res, k, n);return res;}
};
下面三个案例练练手看看。
组合总和 III
class Solution {
public:void backtracking(int pos,int &sum,int n, int k,vector<int> &path ,vector<vector<int>> &re){if (sum == n && path.size() == k) {re.push_back(path);return;}if (path.size() == k) {return;}for (int i = pos;i <= 9;i++) {if (sum + i <= n) {sum += i;path.push_back(i);backtracking(i + 1, sum, n, k, path, re);sum -= i;path.pop_back();}}}vector<vector<int>> combinationSum3(int k, int n) {vector<vector<int>> re;vector<int> path;int sum = 0;backtracking(1, sum, n, k, path, re);return re;}
};
这里可以看到执行的剪枝操作就是,当sum+i大于目标值n的时候就不执行深度递归操作。
组合总和
class Solution {
public:void backtracking(int pos, int sum, int target, vector<vector<int>>& re, vector<int>& candidates,vector<int> path) {if (sum == target) {re.push_back(path);return;}if(pos==candidates.size() || sum>target){return ;}for (int i = pos;i < candidates.size() && sum + candidates[i] <= target;i++) {sum += candidates[i];path.push_back(candidates[i]);backtracking(i, sum, target, re, candidates, path);sum -= candidates[i];path.pop_back();}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {sort(candidates.begin(),candidates.end());vector<vector<int>> re;vector<int> path;backtracking(0, 0, target, re, candidates, path);return re;}
};
组合总和 II
class Solution {
public:void backtracking(int pos,int target, int sum,unordered_map<int, bool> used ,vector<int> path, vector<int> candidates,vector<vector<int>> &re){if (sum == target) {re.push_back(path);return;}for (int i = pos;i < candidates.size() && sum + candidates[i] <= target;i++) {if(i>0 && candidates[i]==candidates[i-1] && used[candidates[i-1]]==false){continue;}sum += candidates[i];path.push_back(candidates[i]);used[candidates[i]] = true;backtracking(i + 1, target, sum,used, path, candidates, re);sum -= candidates[i];path.pop_back();used[candidates[i]] = false;}}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {sort(candidates.begin(), candidates.end());vector<vector<int>> re;vector<int> path;unordered_map<int, bool> used;backtracking(0, target, 0, used, path, candidates, re);return re;}
};
电话号码的字母组合
class Solution {
public:void backtracking(int pos,string path,string digits, vector<string> map, vector<string>& res){if (path.size() == digits.size() ) {res.push_back(path);return;}int num = digits[pos] - '0';for (int i = 0;i < map[num].size();i++) {path.push_back(map[num][i]);backtracking(pos + 1, path, digits, map, res);path.pop_back();}}vector<string> letterCombinations(string digits) {vector<string> map = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};vector<string> res;string path = "";if(digits.size() >0)backtracking(0, path, digits, map, res);return res;}
};
二、分割问题
分割回文串
这个切割问题其实类似上面那些组合问题,其实就是按照字符串的顺序去进行划分子串组合,然后限制条件就是回文串,如果是回文串就添加到路径中,如果不是回文串就跳过。
class Solution {
public:bool is_huiwen(string s) {int i = 0, j = s.size()-1;while (i < j) {if (s[i] != s[j]) {return false;}i++;j--;}return true;}void backtracking(int pos, vector<string> route, string path, string s, vector<vector<string>> &res) {if (pos == s.size()) {res.push_back(route);route.clear();return;}for (int i = pos;i < s.size();i++) {string temp = s.substr(pos,i-pos+1);if (is_huiwen(temp)) {route.push_back(temp);backtracking(i + 1, route, path, s, res);route.pop_back();}}}vector<vector<string>> partition(string s) {vector<vector<string>> res;vector<string> route;string path;backtracking(0, route, path, s, res);return res;}
};
复原 IP 地址
class Solution {
public:bool isvalid(string s) {if(s.empty() || s.size()>3){return false;}if (s.size() >1 &&s[0] == '0') {return false;}int num = stoi(s);if (num > 255 || num<0) {return false;}return true;}void backtracking(int pos,int start, string tmp, string s, vector<string>& res) {if (pos == 3) {string t = s.substr(start, s.size() -pos+ 1);if (isvalid(t)) {tmp += t;res.push_back(tmp);}return;}for (int i = start;i < s.size();i++) {string t = s.substr(start,i-start+1);if (isvalid(t)) {for (int j = 0;j < t.size();j++) {tmp.push_back(t[j]);}tmp.push_back('.');pos++;backtracking(pos, i + 1, tmp, s, res);for (int j = 0;j < t.size();j++) {tmp.pop_back();}tmp.pop_back();pos--;}}}vector<string> restoreIpAddresses(string s) {vector<string> res;string tmp;backtracking(0,0, tmp, s, res);return res;}
};
三、集合问题
对于子集问题,其实跟前面的不同,子集是每一个递归的节点都要进行数据的收集,而前面的组合问题都是有条件限制的才执行收集的操作,比如递归收集数字和为target的组合之类的。
子集
回溯算法:求子集问题!
class Solution {
public:void backtracking(int start_index, vector<int>& nums, vector<int>& path, vector<vector<int>>& res) {res.emplace_back(path);for (int i = start_index;i < nums.size();i++) {path.push_back(nums[i]);backtracking(i + 1, nums, path, res);path.pop_back();}}vector<vector<int>> subsets(vector<int>& nums) {vector<vector<int>> res;vector<int> path;backtracking(0, nums, path, res);return res;}
};
子集 II
这个问题其实是组合总和2的一个派生问题,本质上是差不多的,无法就是需要一个东西来去标记一下使用过的数据,避免下次重新去使用。
class Solution {
public:void backtracking(int pos, unordered_map<int, bool> &used, vector<int>& nums, vector<int>& path, vector<vector<int>>& res) {res.emplace_back(path);for (int i = pos; i < nums.size();i++) {if (i > 0 && nums[i] == nums[i - 1] && used[nums[i - 1]] == false) {continue;}path.push_back(nums[i]);used[nums[i]] = true;backtracking(i + 1, used, nums, path, res);used[nums[i]] = false;path.pop_back();}}vector<vector<int>> subsetsWithDup(vector<int>& nums) {sort(nums.begin(), nums.end());vector<vector<int>> res;vector<int> path;unordered_map<int, bool> used;backtracking(0, used, nums, path, res);return res;}
};
非递减子序列
对于这个问题,也就是说每一层需要去避免重复使用同一个数字,也就是需要在每一个递归节点去创建一个作为标记的哈希表来依次标记已经遍历的节点,这里对比前面使用了纵向哈希表是不一样的,这个是横向的。
class Solution {
public:void backtracking(int pos,vector<int> &path, vector<int>& nums,vector<vector<int>> &res) {if(path.size()>=2)res.emplace_back(path);if (pos == nums.size()) {return;}unordered_map<int, bool> used;for (int i = pos;i < nums.size();i++) {if (path.size() > 0 && path.back() > nums[i]) {continue;}if (used[nums[i]]) {continue;}path.push_back(nums[i]);used[nums[i]] = true;backtracking(i + 1, path, nums,res);path.pop_back();}}vector<vector<int>> findSubsequences(vector<int>& nums) {vector<vector<int>> res;vector<int> path;backtracking(0, path, nums, res);return res;}
};
class Solution {
public:void backtracking(int pos,vector<int> &path, vector<int>& nums,vector<vector<int>> &res) {if(path.size()>=2)res.emplace_back(path);if (pos == nums.size()) {return;}int used[201] = {0};for (int i = pos;i < nums.size();i++) {if (path.size() > 0 && path.back() > nums[i]) {continue;}if (used[nums[i]+100]) {continue;}path.push_back(nums[i]);used[nums[i]+100] = true;backtracking(i + 1, path, nums,res);path.pop_back();}}vector<vector<int>> findSubsequences(vector<int>& nums) {vector<vector<int>> res;vector<int> path;backtracking(0, path, nums, res);return res;}
};
四、排列问题
全排列
class Solution {
public:void backtracking(int pos,unordered_map<int, bool>& used, vector<int>& nums, vector<int>& path, vector<vector<int>>& res) {if (path.size() == nums.size()) {res.emplace_back(path);return;}for (int i = 0;i < nums.size();i++) {if (used[nums[i]] == false) {used[nums[i]] = true;path.push_back(nums[i]);backtracking(i + 1, used,nums, path, res);used[nums[i]] = false;path.pop_back();}}}vector<vector<int>> permute(vector<int>& nums) {vector<vector<int>> res;vector<int> path;unordered_map<int, bool> used;backtracking(0,used, nums, path, res);return res;}
};
全排列 II
class Solution {
public:void backtracking(int pos,unordered_map<int, bool> &map, vector<int> &nums, vector<int> &path, vector<vector<int>>& res) {if (path.size() == nums.size()){res.emplace_back(path);return;}bool used[21] = { false };for (int i = 0;i < nums.size();i++) {if (! used[nums[i]+10] && !map[i]) {used[nums[i] + 10] = true;map[i] = true;path.push_back(nums[i]);backtracking(pos + 1,map, nums, path, res);path.pop_back();map[i] = false;}}}vector<vector<int>> permuteUnique(vector<int>& nums) {unordered_map<int, bool> map;vector<vector<int>> res;vector<int> path;unordered_map<int, bool> used;backtracking(0,map, nums, path, res);return res;}
};
class Solution {
public:void backtracking(int pos,unordered_map<int, bool> &map, vector<int> &nums, vector<int> &path, vector<vector<int>>& res) {if (path.size() == nums.size()){res.emplace_back(path);return;}bool used[21] = { false };for (int i = 0;i < nums.size();i++) {if (! used[nums[i]+10] && !map[i]) {used[nums[i] + 10] = true;map[i] = true;path.push_back(nums[i]);backtracking(pos + 1,map, nums, path, res);path.pop_back();map[i] = false;}}}vector<vector<int>> permuteUnique(vector<int>& nums) {unordered_map<int, bool> map;vector<vector<int>> res;vector<int> path;unordered_map<int, bool> used;backtracking(0,map, nums, path, res);return res;}
};
五、棋盘问题
N 皇后

class Solution {
public:bool isok(int x, int y, int n, vector<string>& path) {// 同一列不同行检查for (int i = 0;i < x;i++) {if (path[i][y] == 'Q') {return false;}}// 右上对角线检查for (int i = x, j = y;i >= 0 && j < n;i--, j++) {if (path[i][j] == 'Q') {return false;}}// 左上对角线检查for (int i = x, j = y;i >= 0 && j >= 0;i--, j--) {if (path[i][j] == 'Q') {return false;}}return true;}void backtracking(int pos,int n, vector<string>& path, vector<vector<string>>& res) {if (pos == n) {res.push_back(path);return;}for (int i = 0;i < n;i++) {if (isok( pos, i, n, path)) {path[pos][i] = 'Q';backtracking(pos + 1, n, path, res);path[pos][i] = '.';}}}vector<vector<string>> solveNQueens(int n) {vector<vector<string>> res;vector<string> path(n, string(n, '.'));backtracking(0, n, path, res);return res;}
};

相关文章:
回溯算法(C/C++)
目录 一、组合问题 组合 组合剪枝 组合总和 III编辑 组合总和编辑 组合总和 II 电话号码的字母组合编辑 二、分割问题 分割回文串 复原 IP 地址 三、集合问题 子集 子集 II 非递减子序列 四、排列问题 全排列 全排列 II 五、棋盘问题 N 皇后 课程&#x…...
物联网智慧农业一体化解决方案-可继续扩展更多使用场景
在智慧农业中,从种子、施肥、灌溉、锄地、农具管理、日常照料到蔬菜档案管理,以及与客户、供应商、市场的对接,可以通过物联网(IoT)、大数据、人工智能(AI)、区块链和云计算等技术,构建一个从生产到销售的全流程数字化、智能化农业生态系统。以下是实现方案和技术路径的…...
Jackson 详解
目录 前言 Jackson 是 Java 生态中最流行的 JSON 处理库之一,广泛应用于 RESTful API、数据存储和传输等场景。它提供了高效、灵活的 JSON 序列化和反序列化功能,支持注解、模块化设计和多种数据格式(如 XML、YAML)。本文将详细介…...
游戏引擎学习第143天
仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾并规划今天的内容 目前,我们正在进行声音混合的开发。我们已经写好了声音混合器,并且已经实现了一些功能,比如声音流播放和音量插值。过去一周我们做了很多工作,进展非常快。不…...
SLAM评估工具安装及使用EVO(Ubuntu20.04安装evo)--缺少 onnx 库还有Pandas 版本不兼容解决
介绍一下我的是ubuntu20.04.机载电脑是orinnx,通过源码烧写的系统。 首先打开终端,输入 pip install evo --upgrade --no-binary evo 安装过程中出现如下问题 缺少 onnx 库还有Pandas 版本不兼容, ONNX(Open Neural Network E…...
Nginx解决前端跨域问题
1. 理解 CORS 和同源策略 1.1 同源策略 同源策略是一种浏览器安全机制,用于阻止不同源(不同域名、协议或端口)的 Web 应用相互访问数据。它确保了 Web 应用的隔离性,防止恶意网站访问用户数据或执行不安全的操作。 同源策略下&…...
ReferenceError: assignment to undeclared variable xxx
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 🍚 蓝桥云课签约作者、…...
国产编辑器EverEdit - 宏功能介绍
1 宏 1.1 应用场景 宏是一种重复执行简单工作的利器,可以让用户愉快的从繁琐的工作中解放出来,其本质是对键盘和菜单的操作序列的录制,并不会识别文件的内容,属于无差别无脑执行。 特别是对一些有规律的重复按键动作,…...
图像滑块对比功能的开发记录
背景介绍 最近,公司需要开发一款在线图像压缩工具,其中的一个关键功能是让用户直观地比较压缩前后的图像效果。因此,我们设计了一个对比组件,它允许用户通过拖动滑块,动态调整两张图像的显示区域,从而清晰…...
【计算机网络】Socket
Socket 是网络通信的核心技术之一,充当应用程序与网络协议栈之间的接口。 1. Socket 定义 Socket(套接字)是操作系统提供的 网络通信抽象层,允许应用程序通过标准接口(如 TCP/IP 或 UDP)进行数据传输。它…...
Electron应用中获取设备唯一ID和系统信息
让我创建一篇关于如何在Electron应用中获取设备唯一ID和系统信息,并在登录时使用这些信息的博客文章。我将确保步骤明确、条理清晰,适合初学者和有经验的开发者。 这篇博客应包含以下部分: 介绍 - 为什么需要获取设备信息前提条件和安装依赖…...
文件上传漏洞:upload-labs靶场11-20
目录 pass-11 pass-12 pass-13 pass-14 pass-15 pass-16 pass-17 pass-18 pass-19 pass-20 pass-11 分析源代码 ,发现上传文件的存放路径可控 if(isset($_POST[submit])){$ext_arr array(jpg,png,gif);$file_ext substr($_FILES[upload_file][name],st…...
国产化板卡设计原理图:2330-基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡
基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡 一、板卡概述 本板卡基于 FPGAJFM7K325T 芯片,pin_to_pin兼容FPGAXC7K410T-2FFG900 ,支持PCIeX8、64bit DDR3容量2GByte,HPC的FMC连接器,板卡支持PXIE标准协议,其中XJ3…...
使用Open WebUI下载的模型文件(Model)默认存放在哪里?
🏡作者主页:点击! 🤖Ollama部署LLM专栏:点击! ⏰️创作时间:2025年2月21日21点21分 🀄️文章质量:95分 文章目录 使用CMD安装存放位置 默认存放路径 Open WebUI下…...
FPGA 配置原理
用户编程控制的FPGA 是通过加载比特位流配置内部的存储单元实现的。该存储单元就是所谓的配置单元,它必须在器件上电后进行配置,从而设置查找表(LUT)的属性、连线方式、IOB 电压标准和其它的用户设计。 1.配置帧 以Xilinx 公司的…...
企业级虚拟化数据库基础平台自动化部署项目
一、项目简介及准备工作 1.1.虚拟化平台简介 1.1.1.ESXi 8 是什么? 定义: ESXi 8 是 VMware 推出的最新版本 裸机虚拟化管理程序(Hypervisor),属于 VMware vSphere 产品线的核心组件。 核心功能: 在物理…...
关于elementui的时间组件与后端时间和oracle数据库时间的对应格式
前端: <el-date-pickerv-model"formInline.startTime"type"date"value-format"yyyy-MM-dd"placeholder"选择日期"> </el-date-picker> 后端: private String startTime; private String endTime…...
一周学会Flask3 Python Web开发-WTForms表单验证
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们可以通过WTForms表单类属性的validators属性来实现表单验证。 常用的WTForms验证器 验证器说明DataRequired(messageNo…...
qt open3dBPA重建
qt open3dBPA重建 效果展示二、流程三、代码效果展示 二、流程 创建动作,链接到槽函数,并把动作放置菜单栏 参照前文 三、代码 1、槽函数实现 void on_actionBPA_triggered();//bpa重建 void MainWindow::...
Unity游戏开发中的网格简化与LOD技术(Mesh Simplification LOD)
在Unity游戏开发中,网格简化(Mesh Simplification)和LOD(Level of Detail)技术是优化渲染性能的关键手段,尤其在处理复杂场景和高精度模型时至关重要。以下是一套系统的实现方案与优化策略: 一、…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...











