回溯算法(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)技术是优化渲染性能的关键手段,尤其在处理复杂场景和高精度模型时至关重要。以下是一套系统的实现方案与优化策略: 一、…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...

Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
LangChain【6】之输出解析器:结构化LLM响应的关键工具
文章目录 一 LangChain输出解析器概述1.1 什么是输出解析器?1.2 主要功能与工作原理1.3 常用解析器类型 二 主要输出解析器类型2.1 Pydantic/Json输出解析器2.2 结构化输出解析器2.3 列表解析器2.4 日期解析器2.5 Json输出解析器2.6 xml输出解析器 三 高级使用技巧3…...