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

文章目录

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


前言


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

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

回溯的核心问题

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

接着看这个题目: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一下的。

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


总结

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


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

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

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes 概述以及Kubernetes 集群架构与组件

目录 Kubernetes概述 K8S 是什么 为什么要用 K8S K8S 的特性 Kubernetes 集群架构与组件 核心组件 Master 组件 Node 组件 ​编辑 Kubernetes 核心概念 常见的K8S按照部署方式 Kubernetes概述 K8S 是什么 K8S 的全称为 Kubernetes,Kubernetes 是一个可移植、可扩…

多种方法解决leetcode经典题目-LCR 155. 将二叉搜索树转化为排序的双向链表, 同时弄透引用变更带来的bug

1 描述 2 解法一: 使用list列表粗出中序遍历的结果&#xff0c;然后再依次处理list中的元素并且双向链接 public Node treeToDoublyList2(Node root) {if(rootnull)return root;Node dummynew Node(-10000);List<Node>ansnew ArrayList<>();dfs2(root,ans);Node p…

Maven本地配置获取nexus私服的依赖

场景 Nexus-在项目中使用Maven私服&#xff0c;Deploy到私服、上传第三方jar包、在项目中使用私服jar包&#xff1a; Nexus-在项目中使用Maven私服&#xff0c;Deploy到私服、上传第三方jar包、在项目中使用私服jar包_nexus maven-releases 允许deploy-CSDN博客 在上面讲的是…

stm32中断

目录 简介 什么是NVIC 中断优先级 EXTI 简介 总结 hal库初始化代码 标准库初始化代码 简介 什么是中断&#xff1f;正常情况下&#xff0c;微处理器根据代码内容&#xff0c;按顺序执行指令。执行过程中&#xff0c;如果遇到其它紧急的事件需要处理&#xff0c;则先暂停当…

在ffmpeg中,如何把h264转换为rgb格式

在ffmpeg中&#xff0c;网络视频流h264为什么默认的转为YUV而不是其他格式 文章中介绍了&#xff0c;h264解码的时候是直接解码为yuv的&#xff0c;如果在使用的过程中 需要用到rgb的格式&#xff0c;我们该如何来转换这种格式呢&#xff1f; 在上面的文章中&#xff0c;我们已…

[架构之路-252/创业之路-83]:目标系统 - 纵向分层 - 企业信息化的呈现形态:常见企业信息化软件系统 - 企业应用信息系统集成

目录 第一章 什么是企业应用信息系统集成What 1.1 简介 1.2 架构 二、为什么需要企业应用信息系统集成Why 三、如何实现企业应用信息系统集成 3.1 步骤 3.2 企业应用集成的层次 3.3 业务流程重组 第一章 什么是企业应用信息系统集成What 1.1 简介 企业应用信息系统集…

【项目源码解析】某3C产品自动光学检测系统

解决方案源码解析思维导图 一、带有桁架机械手的自动光学检测系统介绍 二、关于机械手运动控制&#xff08;是否需要机器人学方面的知识&#xff09; 机械手的运动控制不需要深入了解机器人学方面的知识的情况包括&#xff1a; 预配置和任务单一性&#xff1a;如果机械手已经预…

深入理解元素的高度、行高、行盒和vertical-align

1.块级元素的高度 当没有设置高度时&#xff0c;高度由内容撑开&#xff0c;实际上是由行高撑开&#xff0c;当有多行时&#xff0c;高度为每行的行高高度之和。 行高为什么存在&#xff1f; 因为每行都由一个行盒包裹&#xff0c;行高实际上是行盒的高度。 2.什么是行盒&am…

一天掌握python爬虫【基础篇】 涵盖 requests、beautifulsoup、selenium

大家好&#xff0c;我是python222小锋老师。前段时间卷了一套 Python3零基础7天入门实战 以及1小时掌握Python操作Mysql数据库之pymysql模块技术 近日锋哥又卷了一波课程&#xff0c;python爬虫【基础篇】 涵盖 requests、beautifulsoup、selenium&#xff0c;文字版视频版。1…

使用Objective-C和ASIHTTPRequest库进行Douban电影分析

概述 Douban是一个提供图书、音乐、电影等文化内容的社交网站&#xff0c;它的电影频道包含了大量的电影信息和用户评价。本文将介绍如何使用Objective-C语言和ASIHTTPRequest库进行Douban电影分析&#xff0c;包括如何获取电影数据、如何解析JSON格式的数据、如何使用代理IP技…

网站如何改成HTTPS访问

在今天的互联网环境中&#xff0c;将网站更改成HTTPS访问已经成为了一种标准做法。HTTPS不仅有助于提高网站的安全性&#xff0c;还可以提高搜索引擎排名&#xff0c;并增强用户信任。因此&#xff0c;转换为HTTPS是一个重要的举措&#xff0c;无论您拥有个人博客、电子商务网站…

HarmonyOS(二)—— 初识ArkTS开发语言(上)之TypeScript入门

前言 Mozilla创造了JS&#xff0c;Microsoft创建了TS&#xff0c;而Huawei进一步推出了ArkTS。因此在学习使用ArkTS前&#xff0c;需要掌握基本的TS开发技能。 ArkTS介绍 ArkTS是HarmonyOS优选的主力应用开发语言。它在TypeScript&#xff08;简称TS&#xff09;的基础上&am…

神经网络的解释方法之CAM、Grad-CAM、Grad-CAM++、LayerCAM

原理优点缺点GAP将多维特征映射降维为一个固定长度的特征向量①减少了模型的参数量&#xff1b;②保留更多的空间位置信息&#xff1b;③可并行计算&#xff0c;计算效率高&#xff1b;④具有一定程度的不变性①可能导致信息的损失&#xff1b;②忽略不同尺度的空间信息CAM利用…

【mfc/VS2022】计图实验:绘图工具设计知识笔记3

实现类对串行化的支持 如果要用CArchive类保存对象的话&#xff0c;那么这个对象的类必须支持串行化。一个可串行化的类通常有一个Serialize成员函数。要想使一个类可串行化&#xff0c;要经历以下5个步骤&#xff1a; 1、从CObject派生类 2、重写Serialize成员函数 3、使用DE…

Ubuntu MySQL客户端功能介绍(mysql-client)mysql命令(mysql客户端命令)数据库导出、数据库导入

文章目录 Ubuntu MySQL客户端(mysql-client)功能介绍MySQL客户端与服务端服务器端&#xff08;MySQL Server&#xff09;客户端&#xff08;MySQL Client&#xff09; 安装MySQL客户端连接到MySQL服务器&#xff08;mysql -h host -u user -p&#xff09;执行SQL查询批处理模式…

wordpress上传限制2M修改为256M的两种方式

方式一&#xff1a;修改php.ini 上传文件限制大小主要是php的php.ini配置决定的&#xff0c;所以只要找到php的配置文件&#xff0c;并且修改里面的配置即可&#xff0c;linux查看php的版本和配置文件位置的命令&#xff1a; php -i | grep "Configuration File" 一…

AI智能语音识别模块(二)——基于Arduino的语音控制MP3播放器

文章目录 简介离线语音控制模块Mini MP3模块0.96寸 OLED模块实验准备安装库接线定义主要程序实验效果注意事项总结 简介 在前面一篇文章里我们对AI智能语音识别模块进行了介绍&#xff0c;并对离线语音模组下载固件的过程进行了一个简单描述&#xff0c;不知道大家还记不记得&…

用前端框架Bootstrap和Django实现用户注册页面

01-新建一个名为“mall_backend”的Project 命令如下&#xff1a; CD E:\Python_project\P_001\myshop-test E: django-admin startproject mall_backend02-新建应用并注册应用 执行下面条命令依次创建需要的应用&#xff1a; CD E:\Python_project\P_001\myshop-test\mall…

uniapp如何使用mumu模拟器

模拟器安装 下载地址&#xff1a;MuMu模拟器 模拟器相关设置 1.在设置-显示中选中手机版&#xff0c;设置手机分辨率 2.设置-关于手机-版本号快速点击&#xff0c;将其设置为开发者模式 3.选择多开器 4.打开hbuilderx&#xff0c;找到adb设置 5.配置adb路径及端口号&#x…

Servlet 初始化参数(web.xml和@WebServlet)

1、通过web.xml方式 <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://xmlns.jcp.org/xm…