【回溯算法经典题目解析】

1. 什么是回溯算法

回溯算法是⼀种经典的递归算法,通常用于解决组合问题、排列问题和搜索问题等。

回溯算法的基本思想:从一个初始状态开始,按照⼀定的规则向前搜索,当搜索到某个状态⽆法前进时,回退到前⼀个状态,再按照其他的规则搜索。回溯算法在搜索过程中维护一个状态树,通过遍历状态树来实现对所有可能解的搜索。

回溯算法的核心思想:“试错”,即在搜索过程中不断地做出选择,如果选择正确,则继续向前搜 索;否则,回退到上一个状态,重新做出选择。回溯算法通常⽤于解决具有多个解,且每个解都需要 搜索才能找到的问题。

2. 回溯算法的模板

void backtrack(vector<int>& path, vector<int>& choice, ...) {// 满足结束条件if (/*满足结束条件*/) {// 将路径添加到结果集中res.push_back(path);return;}// 遍历所有选择for (int i = 0; i < choices.size(); i++) {// 做出选择path.push_back(choices[i]);// 做出当前选择后继续搜索}backtrack(path, choices);// 撤销选择path.pop_back();
}

其中, path 表⽰当前已经做出的选择, choices 表⽰当前可以做的选择。在回溯算法中,我们需 要做出选择,然后递归地调⽤回溯函数。如果满⾜结束条件,则将当前路径添加到结果集中;否则, 我们需要撤销选择,回到上⼀个状态,然后继续搜索其他的选择。 回溯算法的时间复杂度通常较高,因为它需要遍历所有可能的解。但是,回溯算法的空间复杂度较低,因为它只需要维护⼀个状态树。在实际应用中,回溯算法通常需要通过剪枝等方法进⾏优化,以减少搜索的次数,从⽽提高算法的效率。

3. 回溯算法的应用

  • 组合问题

  • 排列问题

  • 子集问题

总结

回溯算法是⼀种⾮常重要的算法,可以解决许多组合问题、排列问题和搜索问题等。回溯算法的核心思想是搜索状态树,通过遍历状态树来实现对所有可能解的搜索。回溯算法的模板非常简单,但是实现起来需要注意⼀些细节,比如如何做出选择、如何撤销选择等。

4. 全排列

首先我们看到这个题目,立马就会想到使用穷举来解决,依次选取一个位置来枚举,但是假如我们这个题目给的数组是100个,那么岂不要写100个for循环来枚举,太麻烦了,我们使用dfs来解决,使用dfs之前我们首先要画出我们的决策树,我们以实例一为例。

 此时就要用到我们的递归方法,当我们使用递归的时候,我们仅需关心某一个节点在干什么事情,只要我们当前的节点没有使用过,我们就可以添加进去,同时还要注意几个细节问题,

那我们就可以直接来写代码了。

class Solution {vector<int> path;vector<vector<int>> ret;bool check[7] = {false}; // 标记下标是否已经使用
public:vector<vector<int>> permute(vector<int>& nums) {dfs(nums);return ret;}void dfs(vector<int>& nums){if(path.size() == nums.size()){ret.push_back(path);return;}for(int i = 0; i < nums.size(); i++){if(check[i] == false){path.push_back(nums[i]);check[i] = true;dfs(nums);// 回溯 -> 恢复现场path.pop_back();check[i] = false;}}}
};

5. 子集

经过之前的全排列我们可以知道,要想解决一个递归的题目,我们就必须要画好我们的决策树,决策树画的越详细,题目越容易做出来,这个题目有两种决策树,我们分别画出来分别写一下代码。第一个决策树是根据元素选与不选来抉择的。

此时我们设计dfs函数的时候,有两个参数dfs(nums, i),nums就表示数组,而i就是表示是否选择这个下标对应的值,当我们选择的时候,就可以直接将nums[i]添加到path中,然后递归到dfs(nums, i+1)下一层,如果不选,就直接递归dfs(nums, i+1)下一层,当我们到叶子节点的时候,就可以返回啦!

class Solution {vector<vector<int>> ret;vector<int> path;
public:vector<vector<int>> subsets(vector<int>& nums) {dfs(nums, 0);return ret;}void dfs(vector<int>& nums, int pos){if(pos == nums.size()){ret.push_back(path);return;}// 选path.push_back(nums[pos]);dfs(nums, pos + 1);path.pop_back(); // 恢复现场// 不选dfs(nums, pos + 1);}
};

我们在来画出另外一种决策树,依据每层元素的个数来画,每次选完当前元素时,就从后面元素继续选择。

根据上面的决策树,每一层的节点都是枚举后面的数,所以我们的函数设计时dfs(nums, pos),pos当前层枚举的数,此时我们就要来一个for循环解决,同时我们还能发现此时我们不需要bool数组来检查元素是否被使用过,因为我们通过pos控制每一层的节点都是枚举后面的数,不会美剧到已经使用过的值,那什么时候统计结果呢?在进入for循环之前就可以直接统计结果啦。

class Solution {vector<vector<int>> ret;vector<int> path;
public:vector<vector<int>> subsets(vector<int>& nums) {dfs(nums, 0);return ret;}void dfs(vector<int>& nums, int pos){ret.push_back(path);for(int i = pos; i < nums.size(); i++){path.push_back(nums[i]); dfs(nums, i + 1);path.pop_back(); // 恢复现场}}
};

6. 找出所有子集的异或总和再求和

这个题目其实就是子集题目的变种题目,我们依然是使用的dfs来求出子集,只不过此时我们不需要再创建数组存储每个子集,只需要使用一个变量path存储子集的异或结果和一个变量ret存储所有子集的疑惑和结果即可。

class Solution {int ret = 0;int path = 0;
public:int subsetXORSum(vector<int>& nums) {dfs(nums, 0);return ret;}void dfs(vector<int>& nums, int pos){ret += path;for(int i = pos; i < nums.size(); i++){path ^= nums[i];dfs(nums, i + 1);path ^= nums[i]; // 恢复现场}}
};

7. 全排列 Ⅱ

首先我们这个题目是全排列的一个变种题目,这个题目中要求全排列之后不能出现重复的值,因此我们可以按照上面求全排列的方法求出结果,然后再利用set容器进行去重来解决这个题目,但是比较繁琐,我们可以利用剪枝来求解,我们先画出我们的决策树。

此时我们就可以有两种判断方法,只关心合法的分支,合法我们才去dfs,合法的肯定是当前位置并没有被使用,所以check[i] == false,同时我们还要满足,nums[i]不能和nums[i-1]位置的元素相等,但是我们可以看到左边的分支,如果check[i-1]的位置是已经被使用了,那么虽然nums[i]和nums[i-1]位置的元素相等,我们仍然可以使用这个元素,因为这两个元素不在同一层,最后还有一个条件,如果i==0,那么此时我们肯定是合法的,此时可以直接添加。

此时我们就可以来直接写代码啦!

class Solution {vector<vector<int>> ret;vector<int> path;bool check[8] = { false };
public:vector<vector<int>> permuteUnique(vector<int>& nums) {sort(nums.begin(), nums.end()); // 先排序dfs(nums);return ret;}void dfs(vector<int>& nums){if(path.size() == nums.size()){ret.push_back(path);}for(int i = 0; i < nums.size(); i++){// 剪枝if((i == 0 || nums[i-1] != nums[i] || check[i - 1] == true) && check[i] == false){path.push_back(nums[i]);check[i] = true;dfs(nums);// 恢复现场path.pop_back();check[i] = false;}}}
};

8. 电话号码的字母组合

看到这个题目,我们首先想到的就是for循环去解决问题,但是如果传入的digits特别多,那么就要使用很多的for循环,因此这个题目我们也可以使用dfs来解决,首先我们要做的就是画出我们的决策树。

首先我们就需要根据电话进行数字与字母的映射,我们这里直接创建一个数组即可,随后的工作就和全排列的思路差不多,直接上手代码。

class Solution {string hash[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};string path;vector<string> ret;
public:vector<string> letterCombinations(string digits) {if(digits.empty())return ret;dfs(digits, 0);return ret;}void dfs(string& digits, int pos){if(pos == digits.size()){ret.push_back(path);return;}string tmp = hash[digits[pos] - '0'];for(int i = 0; i < tmp.size(); i++){path += tmp[i];dfs(digits, pos + 1);path.pop_back(); // 恢复现场}}
};

9. 括号生成

对于这样的问题,我们首先要了解的就是什么是有效的括号组合呢?第一就是左括号的数量等于右括号的数量,其次是从头开始的任意一个字串,左括号的数量大于右括号的数量。接下来我们就要做的就是画出决策树,决策树画的越详细,题目越容易做出来。

随后我们就可以直接来手写代码啦!!!

class Solution {vector<string> ret;string path;int left = 0;int right = 0;int n1 = 0;
public:vector<string> generateParenthesis(int n) {n1 = n;dfs();return ret;}void dfs(){if(n1 == right){ret.push_back(path);return;}// 选左括号// 左括号的数量小于nif(left < n1){path += '(';left++;dfs();path.pop_back(); // 恢复现场left--;}// 选右括号// 右括号的数量小于左括号的数量if(left > right){path += ')';right++;dfs();path.pop_back(); // 恢复现场right--;}}
};

10. 组合

这个题目其实就是上面的子集的变种题目,我们依然是先来画出我们的决策树。

随后我们就直接来写代码啦。

class Solution {vector<vector<int>> ret;vector<int> path;int n1, k1;
public:vector<vector<int>> combine(int n, int k) {n1 = n;k1 = k;dfs(0);return ret;}void dfs(int pos){if(k1 == path.size()){ret.push_back(path);return;}for(int i = pos; i < n1; i++){path.push_back(i + 1);dfs(i + 1);path.pop_back(); // 恢复现场}}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/39323.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cesium 聚合

cesium 聚合(下面附有源码) 示例代码 <html lang="en"><head><!-- Use correct character set. -->

【智能算法】目标检测算法

目录 一、目标检测算法分类 二、 常见目标检测算法及matlab代码实现 2.1 R-CNN 2.1.1 定义 2.1.2 matlab代码实现 2.2 Fast R-CNN 2.2.1 定义 2.2.2 matlab代码实现 2.3 Faster R-CNN 2.3.1 定义 2.3.2 matlab代码实现 2.4 YOLO 2.4.1 定义 2.4.2 matlab代码实现…

电信NR零流量小区处理

【摘要】随着目前网络建设逐步完善&#xff0c;5G用户的不断发展&#xff0c;针对零流量小区的分析及处理存在着必要性&#xff0c;零流量小区的出现既是用户分布及行为的直观体现&#xff0c;也是发展用户的一个指引&#xff0c;同时也能发现设备的一些故障。一个站点的能够带…

【数值计算库-超长笔记】Python-Mpmath库:高精度数值计算

原文链接&#xff1a;https://www.cnblogs.com/aksoam/p/18279394 更多精彩&#xff0c;关注博客园主页&#xff0c;不断学习&#xff01;不断进步&#xff01; 我的主页 csdn很少看私信&#xff0c;有事请b站私信 博客园主页-发文字笔记-常用 有限元鹰的主页 内容&#xf…

燃料电池混合电源的能量管理系统

这个例子显示了燃料电池混合电源的能量管理系统。 这个例子展示了燃料电池混合电源的能量管理系统。 电路描述 本文给出了基于燃料电池的多电动飞机应急动力系统的仿真模型。随着MEA中起落架和飞控系统的电气化程度的提高&#xff0c;常规应急电源系统(冲压式空气涡轮或空气驱…

代码随想录算法训练营第20天 | 题目: 235. 二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点

代码随想录算法训练营第20天 | 题目&#xff1a; 235. 二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点 文章来源&#xff1a;代码随想录 题目名称&#xff1a; 235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的…

Centos7部署Mysql8.0超级详细教程,一看就会!

1、准备 下载 Mysql 安装包源信息,去到这个网址&#xff0c;https://dev.mysql.com/downloads/repo/yum/ 复制红色框的内容&#xff0c; 2、开始安装 # 以下所有操作建议切换到 root 用户后运行。。yum install wget -y# 将上面的复制内容粘贴到后面&#xff0c;格式&…

前端性能优化-实测

PageSpeed Insights 性能测试 今天测试网站性能的时候发现一个问题&#xff0c;一个h2标签内容为什么会占据这么长的渲染时间&#xff0c;甚至有阶段测到占据了7000多毫秒&#xff0c;使用了很多方法都不能解决&#xff0c;包括了修改标签&#xff0c;样式大小等&#xff0c;当…

JAVA连接FastGPT实现流式请求SSE效果

FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排&#xff0c;从而实现复杂的问答场景&#xff01; 一、先看效果 真正实流式请求&#xff0c;SSE效果&#xff0c;SSE解释&am…

CentOS7环境下DataX的安装、使用及问题解决

DataX概述 DataX 是阿里巴巴开源的一个异构数据源离线同步工具&#xff0c;致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。 为了解决异构数据源同步问题&#xff0c;DataX将复杂的网状的同步链路变…

eclipse断点调试(用图说话)

eclipse断点调试&#xff08;用图说话&#xff09; debug方式启动项目&#xff0c;后端调试bug调试 前端代码调试&#xff0c;请参考浏览器断点调试&#xff08;用图说话&#xff09; 1、前端 选中一条数据&#xff0c;点击删除按钮 2、后端接口打断点 断点按钮 介绍 resum…

236、二叉树的最近公共祖先

前提&#xff1a; 所有 Node.val 互不相同 。p ! qp 和 q 均存在于给定的二叉树中。 代码如下&#xff1a; class Solution { public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if (root q || root p || root NULL) return root;TreeN…

.NET周刊【6月第5期 2024-06-30】

国内文章 呼吁改正《上海市卫生健康信息技术应用创新白皮书》 C# 被认定为A 组件 的 错误认知 https://www.cnblogs.com/shanyou/p/18264292 近日&#xff0c;《上海市卫生健康“信息技术应用创新”白皮书》发布&#xff0c;提到医疗信创核心应用适配方法及公立医院信息系统…

书城在线系统:基于Java和SSM框架的高效信息管理平台

开头语&#xff1a;你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有相关需求&#xff0c;文末可以找到我的联系方式。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM框架&#xff08;Spring, Spring MVC, Mybatis&#xff09; 工具&…

eNSP中WLAN的配置和使用

一、基础配置 1.拓扑图 2.VLAN和IP配置 a.R1 <Huawei>system-view [Huawei]sysname R1 GigabitEthernet 0/0/0 [R1-GigabitEthernet0/0/0]ip address 200.200.200.200 24 b.S1 <Huawei>system-view [Huawei]sysname S1 [S1]vlan 100 [S1-vlan100]vlan 1…

使用瀚高数据库开发管理工具进行数据的备份与恢复---国产瀚高数据库工作笔记008

使用瀚高数据库,备份 恢复数据 然后找到对应的目录 其实就是hgdbdeveloper,瀚高的数据库开发管理工具 对应的包中有个dbclient 这个目录,选中这个目录以后,就可以了,然后 在对应的数据库,比如 data_middle 中,选中 某个模式,比如bigdata_huiju 然后右键进行,点击 恢复,然…

Verilog开源项目——百兆以太网交换机(五)TCAM单元设计

Verilog开源项目——百兆以太网交换机&#xff08;五&#xff09;TCAM单元设计 &#x1f508;声明&#xff1a;未经作者允许&#xff0c;禁止转载 &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN主页 &#x1f511;全新原创以太网交换机项目&#xff0c;Blog内容将聚焦整体架…

核心实验:基于Web前端的性能测试分析!

实验简介 本实验主要利用IE和Chrome的F12开发人员工具结合Web前端测试分析相关知识&#xff0c;对常见网站进行基于前端的性能测试分析&#xff0c;本实验将不会使用到测试开发相关技术&#xff0c;而是纯粹意义上的手工测试&#xff0c;但却是很容易找到系统前端性能及设计问…

C++字体库开发

建议根据字体需求&#xff0c;多个组合使用。高度定制可基于freeTypeharfbuzz基础库完成。 GitHub - GNOME/pango: Read-only mirror of https://gitlab.gnome.org/GNOME/pango GitHub - googlefonts/fontview: Demo app that displays fonts with a free/libre/open-source …

实用软件分享-----一款免费的投屏软件(支持手机投屏到电脑)Aiseesoft Phone Mirror 2.2.36 x64

专栏介绍:本专栏主要分享一些实用的软件(Po Jie版); 声明1:软件不保证时效性;只能保证在写本文时,该软件是可用的;不保证后续时间该软件能一直正常运行;不保证没有bug;如果软件不可用了,我知道后会第一时间在题目上注明(已失效)。介意者请勿订阅。 声明2:本专栏的…