算法通过村第十八关-回溯|青铜笔记|什么叫回溯(中篇)

文章目录

  • 前言
  • 回溯的核心问题
  • 撤销操作解释
  • 总结


前言


提示:阳光好的时候,会感觉还可以活很久,甚至可以活出喜悦。 --余秀华

回溯是非常重要的算法思想之一,主要解决一些暴力枚举也搞不定的问题(这里埋个坑💣)例如组合、分割、子集、棋盘等等。从性能角度来看回溯算法的效率并不是很高,但是对于暴力也解决不了的问题,它往往很快可以出结果,效率低就可以理解了吧。接下来,就看看回溯的事情吧🤩

回溯的核心问题

递归+局部枚举+放下前任

接着看这个题目:77. 组合 - 力扣(LeetCode)


当n = 4时,我们可以选择的有n有{1,2,3,4}这四种情况,所以我们从第一层到第二层的分支有四个,分别表示可取1,2,3,4.而且这里从左到右取数,取过的数,不在重复取。第一次取1,集合变为2,3,4.因为k= 2,我们也只能再取1个数就可以了,分别取2,3,4.得到的集合就是[1,2]、[1,3]、[1,4]。一次类推:

横向:

在这里插入图片描述
每次从集合中选取元素,可选择的范围回逐步收缩,到了取4时就直接返为空了。

继续观察树结构,可以发现,图中每次访问到一个叶子节点(图中的标记处),我们就可以得到一个结果。虽然到最后时空的,但是不影响最终结果。这里相当于从根节点开始每次选择的内容(分支)达到叶子节点时,将其收起起来就是我们想要的结果。

你可以尝试画下n=5,k=3的结果:有没有感觉

在这里插入图片描述
从图中我们发现元素个数n相当于树的宽度(横向),而每个结果的元素个数k相当于树的深度(纵向)。所以我们说回溯问题就是一纵一横而已。在分析我们回发现下面几个规律:

  • 我们每次选择都是从类似{1,2,3,4},{1,2,3,4}这个样的序列中一个一个选的,这就是为什么说是局部枚举,后面的枚举范围回越来越小。
  • 枚举时,我们就是简单的暴露测试而已,一个一个验证,能否满足要求,从上图可以看到,这就是N叉树的遍历过程,一次代码相似度很高。
  • 我们再看叶子过程,这部分是不是和(n = 4,k = 2)处理的结果一致,也就说是很明显的递归结构。

到此,我们还有一个问题待解决,就是回溯一般会有一个手动撤销的操作,为什么要这么做呢?

继续观察纵横图:
在这里插入图片描述
我们可以看到,我们收集的每个结果不是针对叶子节点的,而是针对树枝的,比如最上层我们首先确定了1,下层我们如果选择了2,那么结果就是{1,2},如果选择了3,结果就是{1,3}.一次类推。现在时怎么确定得到第一个结果时{1,2}之后,得到第二个结果是{1,3}呢?

继续观察纵横图 ,可以看到,我们在得到{1,2}之后将2撤销,再继续使用3,就得到了{1,3},同理也可以得到{1,4},之后这一层就没有了,我们可以撤销1,继续从最上层取2继续进行。

这里对应的代码操作就是先将第一个结果放在临时列表的Path里面,得到第一个结果{1,2},之后就将path里面的内容放进结果列表resultList中,之后,将path里面的2撤销掉,继续寻找下一个结果{1,3},继续将path加入resultList中,然后再次撤销,继续寻找。

现在了解为什么要撤销,看图。这个过程,我们称它“放下前任,继续前进”,后面的回溯大都是这样的思路。

这几条就是回溯的基本规律,了解这一点,我们就可以看看代码是怎么回事了,到此我们看看完整的代码时怎样的:

 public List<List<Integer>> combine(int n, int k) {List<List<Integer>> res = new ArrayList<>();if (k <=0 || n < k){return res;}Deque<Integer> path = new ArrayDeque<>();dfs(n,k,1,path,res);return res;}public void dfs(int n,int k,int startIndex,Deque<Integer> path,List<List<Integer>> res){// 递归终止条件,path 等于k(刚好完成一次if (path.size() == k){res.add((new ArrayList<>(path)));return;}// 针对每个节点,这里就是遍历搜索,也就是枚举for(int i = startIndex; i <= n; i++){// 向路径里添加一个数(分支里的取值path.addLast(i);// 搜索起点要加1,就是缩小范围,准备下一轮递归(这里不允许重复dfs(n,k,i + 1,path,res);// 递归之后要做同样的逆向操作,撤销掉path.removeLast();}}

上面代码还有一个问题需要解释:startIndex和i是怎么变化的,为什么要传递到下一层(+ 1)

我们来看看这里的递归循环:

   // 针对每个节点,这里就是遍历搜索,也就是枚举for(int i = startIndex; i <= n; i++){// 向路径里添加一个数(分支里的取值path.addLast(i);// 搜索起点要加1,就是缩小范围,准备下一轮递归(这里不允许重复dfs(n,k,i + 1,path,res);// 递归之后要做同样的逆向操作,撤销掉path.removeLast();}

这里的循环有什么作用呢?看看这里,其实来说是一个枚举,第一次n=4,可以选择1,2,3,4四种情况,所以就存在4个分支,for就需要执行4次:
在这里插入图片描述
对于第二层来说,第一个数,选择1之后,身下的元素就只有2,3,4了。所以这时候for循环就执行3次,后面的只有2次和1次了。

撤销操作解释

如果你还是不清楚的话,这里就带图详细的走一遍,回溯也就是这个地方有点晕,拿下就是胜利✌。

   // 针对每个节点,这里就是遍历搜索,也就是枚举for(int i = startIndex; i <= n; i++){// 向路径里添加一个数(分支里的取值path.addLast(i);// 搜索起点要加1,就是缩小范围,准备下一轮递归(这里不允许重复dfs(n,k,i + 1,path,res);// 递归之后要做同样的逆向操作,撤销掉path.removeLast();}

为什么要remove呢?看下图,当第一层取1时,最底层的遍历从左到右执行,取2,取3,取4.而取3的时候,此时res里面存储的是{1,2},所以这里前提是需要撤销掉2(path.removeLast();)的作用。

这里我们拆解一下递归方法,将递归拆分成函数调用,输出第一条路径{1,2}的步骤如下:
在这里插入图片描述
我们在递归说过一个特点:不到南墙不回头,回溯也是这个道理,我们看看这个过程。

在这里插入图片描述
然后,怎么在次进行呢,这里就需要撤回一下了,但是如果将这里的remove代码去掉,就会是这个样子:
在这里插入图片描述
这里知道遍历完成,然会的就不做撤销,就会下一场进入for循环,也就是回变成{1,2,3}.

path是一个全局的引用的,各个递归的函数是公用的,所以这里处理完{1,2},之后,需要把2撤出去,将1保留,再让三进入,从而得到{1,3}.所以这里不许remove一下的。

同样处理完之后,后续的也是依次处理,需要撤销的,这里也就是不得不处理撤销的操作。


总结

提示:什么叫回溯;保留状态;撤销操作;回溯核心思想;回溯的入门


如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

如有不理解的地方,欢迎你在评论区给我留言,我都会逐一回复 ~

也欢迎你 关注我 ,喜欢交朋友,喜欢一起探讨问题。

在这里插入图片描述

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

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

相关文章

NTFS文件系统解析(三)

1、引言 对于NTFS文件系统而言&#xff0c;无论文件内容本身&#xff0c;抑或真实的文件属性&#xff0c;都被称之为属性。 而正如前文说到的&#xff0c;NTFS预定义了16种属性用于文件系统的管理。 而通常情况下&#xff0c;往往只需要关注其中的某些属性即可。 2、属性头 …

云智慧联合北航提出智能运维(AIOps)大语言模型及评测基准

随着各行业数字化转型需求的不断提高&#xff0c;人工智能、云计算、大数据等新技术的应用已不仅仅是一个趋势。各行业企业和组织纷纷投入大量资源&#xff0c;以满足日益挑剔的市场需求&#xff0c;追求可持续性和竞争力&#xff0c;这也让运维行业迎来了前所未有的挑战和机遇…

数据结构-二叉树·堆(顺序结构的实现)

&#x1f389;个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名乐于分享在学习道路上收获的大二在校生 &#x1f43b;‍❄个人主页&#x1f389;&#xff1a;GOTXX &#x1f43c;个人WeChat&#xff1a;ILXOXVJE&#x1f43c;本文由GOTXX原创&#xff0c;首发CSDN&…

【wp】2023鹏城杯初赛 Web web1(反序列化漏洞)

考点&#xff1a; 常规的PHP反序列化漏洞双写绕过waf 签到题 源码&#xff1a; <?php show_source(__FILE__); error_reporting(0); class Hacker{private $exp;private $cmd;public function __toString(){call_user_func(system, "cat /flag");} }class A {p…

Ansible中的角色使用

Ansible中的角色使用&#xff1a; 目录 一、ansible角色简介 二、roles目录结构 三、roles的创建 四、roles的使用 1、书写task主任务 2、触发器模块 3、变量模块 4、j2模块 5、files模块 6、启用模块 7、执行playbook 五、控制任务执行顺序 六、多重角色的使用 一…

数据结构(超详细讲解!!)第二十一节 特殊矩阵的压缩存储

1.压缩存储的目标 值相同的元素只存储一次 压缩掉对零元的存储&#xff0c;只存储非零元 特殊形状矩阵&#xff1a; 是指非零元&#xff08;如值相同的元素&#xff09;或零元素分布具有一定规律性的矩阵。 如&#xff1a; 对称矩阵 上三角矩阵 下三角矩阵 对角矩阵 准…

redis教程 二 redis客户端Jedis使用

文章目录 Redis的Java客户端-JedisJedis快速入门创建工程&#xff1a;引入依赖&#xff1a;建立连接测试&#xff1a;释放资源Jedis连接池创建Jedis的连接池改造原始代码 Redis的Java客户端-SpringDataRedis快速入门导入pom坐标配置文件测试代码 数据序列化器StringRedisTempla…

【unity实战】Unity实现2D人物双击疾跑

最终效果 前言 我们要实现的功能是双击疾跑&#xff0c;当玩家快速地按下同一个移动键两次时能进入跑步状态 我假设快速按下的定义为0.2秒内&#xff0c;按下同一按键两次 简单的分析一下需求&#xff0c;实现它的关键在于获得按键按下的时间&#xff0c;我们需要知道第一次…

STM32G030F6P6 芯片实验 (二)

STM32G030F6P6 芯片实验 (二) Hello World - GPIO LED 尝试了下, 从 0 开始建 MDK HAL M0plus Project, 成功点亮 LED了。 但是 ST-LINK跑着跑着, 码飞了! 不知飞哪去了。 只好拿 MX 建了个 MDK Base。 呼叫 SysTick HAL_Delay(), 切换 LED。 基本上都是一样的用法, 只是换…

MapReduce WordCount程序实践(IDEA版)

环境 Linux&#xff1a;Hadoop2.x Windows&#xff1a;jdk1.8、Maven3、IDEA2021 步骤 编程分析 编程分析包括&#xff1a; 1.数据过程分析&#xff1a;数据从输入到输出的过程分析。 2.数据类型分析&#xff1a;Map的输入输出类型&#xff0c;Reduce的输入输出类型&#x…

蓝桥杯每日一题2023.11.2

题目描述 等差素数列 - 蓝桥云课 (lanqiao.cn) 题目分析 对于此题我们需要求出最小的公差并且长度为10&#xff0c; 1.确保序列开始为素数 2.确定枚举的个数 注意&#xff1a;序列中数只是d的变化&#xff0c;可以通过此计算将开始数字后9个数字都计算出来&#xff0c;d是…

【Qt之QtXlsx模块】安装及使用

1. 安装Perl&#xff0c;编译QtXlsx源码用 可以通过命令行进行查看是否已安装Perl。 下载及安装传送门&#xff1a;链接: https://blog.csdn.net/MrHHHHHH/article/details/134233707?spm1001.2014.3001.5502 1.1 未安装 命令&#xff1a;perl --version 显示以上是未安装…

C#中LINQtoSQL只能在.NetFramework下使用,不能在.net 下使用

目录 一、在net7.0下无法实现LINQtoSQL 1.VS上建立数据库连接 2.VS上创建LINQtoSQL 二、在.NetFramework4.8下成功实现LINQtoSQL 1.VS上建立数据库连接 2.VS上创建LINQtoSQL 三、结论 四、理由 本文是个人观点&#xff0c;因为我百般努力在.net7.0下无法实现LINQtoSQL的…

海康Visionmaster-全局脚本:方案加载完成信号发给通 信设备的方法

需要在方案加载完成后&#xff0c;发送加载完成信号到全局变量&#xff0c;发送给通信设备。 全局脚本的使用可以通过打开示例&#xff0c;完成常用的基本功能开发。 打开全局通信代码后&#xff0c;在脚本中添加代码

springboot前后端时间类型传输

springboot前后端时间类型传输 前言1.java使用时间类型java.util.Date2.java使用localDateTime 前言 springboot前后端分离项目总是需要进行时间数据类型的接受和转换,针对打代码过程中不同的类型转化做个总结 1.java使用时间类型java.util.Date springboot的项目中使用了new …

Proteus仿真--1602LCD显示电话拨号键盘按键实验(仿真文件+程序)

本文主要介绍基于51单片机的LCD1602显示电话拨号键盘按键实验&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 其中右下方12个按键模拟仿真手机键盘&#xff0c;使用方法同手机键一样&#xff0c;拨打手机号码则在液晶显示屏上显示对应的号码 仿真运行…

DDoS类型攻击对企业造成的危害

超级科技实验室的一项研究发现&#xff0c;每十家企业中&#xff0c;有四家(39%)企业没有做好准备应对DDoS攻击&#xff0c;保护自身安全。且不了解应对这类攻击最有效的保护手段是什么。 由于缺乏相关安全知识和保护&#xff0c;使得企业面临巨大的风险。 当黑客发动DDoS攻击…

译文:我们如何使 Elasticsearch 7.11 中的 date_histogram 聚合比以往更快

这篇文章是ES7.11版本的文章&#xff0c;主要学习的是思路&#xff0c;记录在这里留作以后参考用。 原文地址&#xff1a;https://www.elastic.co/cn/blog/how-we-made-date-histogram-aggregations-faster-than-ever-in-elasticsearch-7-11 正文开始&#xff1a; Elasticsea…

【数组】有序数组的平方

## 977.有序数组的平方 力扣题目链接 (opens new window) 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10]输出&#xff1a;[0,…

初阶JavaEE(15)(Cookie 和 Session、理解会话机制 (Session)、实现用户登录网页、上传文件网页、常用的代码片段)

接上次博客&#xff1a;初阶JavaEE&#xff08;14&#xff09;表白墙程序-CSDN博客 Cookie 和 Session 你还记得我们之前提到的Cookie吗&#xff1f; Cookie是HTTP请求header中的一个属性&#xff0c;是一种用于在浏览器和服务器之间持久存储数据的机制&#xff0c;允许网站…