递归、搜索与回溯算法(专题二:深搜)

往期文章(希望小伙伴们在看这篇文章之前,看一下往期文章)

(1)递归、搜索与回溯算法(专题零:解释回溯算法中涉及到的名词)【回溯算法入门必看】-CSDN博客

(2)递归、搜索与回溯算法(专题一:递归)-CSDN博客

 深搜是实现递归的一种方式,接下来我们之间从题入手,深入浅出地了解深搜吧!

目录

1. 计算布尔二叉树的值

2. 求根结点到叶结点的数字之和

3. 二叉树剪枝

4. 验证二叉搜索树

5. 二叉搜索树中第k小的元素

​​6. 二叉树的所有路径


1. 计算布尔二叉树的值

力扣题目链接

 

解析: 

(1)函数头设计

需要根结点,故函数头为:dfs(TreeNode root);

(2)函数体设计(无条件相信这个“黑盒”,他能帮你将每个相同的子问题稳妥地解决!!!)

① 接收左子树传过来的值:boolean leftTree = dfs(root.left);       

② 接收右子树传过来的值:boolean rightTree = dfs(root.right);

③ leftTree ——> root.val <—— rightTree 得到最终结果。

(3)递归出口

到叶子结点就是应该结束递归,开始回溯。

    public boolean evaluateTree(TreeNode root) {//递归出口//到了叶子结点if(root.left == null){if(root.val == 0)return false;elsereturn true;}//开始递归boolean leftTree = evaluateTree(root.left);boolean rightTree = evaluateTree(root.right);if(root.val == 2){return leftTree || rightTree;}else{return leftTree && rightTree;}}

2. 求根结点到叶结点的数字之和

力扣题目链接

解析:

前序遍历按照根节点、左⼦树、右⼦树的顺序遍历⼆叉树的所有节点,通常⽤于⼦节点的状态依赖于⽗节点状态的题⽬。
在前序遍历的过程中,我们可以往左右⼦树传递信息,并且在回溯时得到左右⼦树的返回值。递归函数可以帮我们完成两件事:
(1)将⽗节点的数字与当前节点的信息整合到⼀起,计算出当前节点的数字,然后传递到下⼀层进⾏递
归;
(2)当遇到叶⼦节点的时候,就不再向下传递信息,⽽是将整合的结果向上⼀直回溯到根节点。
在递归结束时,根节点需要返回的值也就被更新为了整棵树的数字和。

递归函数设计:int dfs(TreeNode root,int num)
(1)返回值:当前⼦树计算的结果(数字和);

(2)参数num:递归过程中往下传递的信息(⽗节点的数字);
(3)函数作⽤:整合⽗节点的信息与当前节点的信息计算当前节点数字,并向下传递,在回溯时返回当前⼦树(当前节点作为⼦树根节点)数字和。

递归函数流程:
(1)当遇到空节点的时候,说明这条路从根节点开始没有分⽀,返回0;
(2)结合⽗节点传下的信息以及当前节点的val,计算出当前节点数字sum;
(3)如果当前结点是叶⼦节点,直接返回整合后的结果sum;
(4)如果当前结点不是叶⼦节点,将?sum?传到左右⼦树中去,得到左右⼦树中节点路径的数字和,然后相加后返回结果。

① 将自身的值并入;②  将自己本身的数字并入后走左子树;③ 将自己本身的数字并入后走右子树;④ 左右子树的结果,然后向上返回。

    public int sumNumbers(TreeNode root) {return dfs(root,0);//从个位数开始}public int dfs(TreeNode root,int perSum){//一:将自身的值并入perSum = perSum * 10 + root.val;//四:递归出口:看看是不是叶子结点了,如果是,就向上返回if(root.left == null && root.right == null){return perSum;}//二:走左子树int ret = 0;if(root.left != null){ret += dfs(root.left,perSum);}//三:走右子树if(root.right != null){ret += dfs(root.right,perSum);}return ret;}

3. 二叉树剪枝

力扣题目链接

解析:

后序遍历按照左⼦树、右⼦树、根节点的顺序遍历⼆叉树的所有节点,通常⽤于⽗节点的状态依赖于⼦节点状态的题⽬。
(1)如果我们选择从上往下删除,我们需要收集左右⼦树的信息,这可能导致代码编写相对困难。然⽽,通过观察我们可以发现,如果我们先删除最底部的叶⼦节点,然后再处理删除后的节点,最终的结果并不会受到影响。
(2)因此,我们可以采⽤后序遍历的⽅式来解决这个问题。在后序遍历中,我们先处理左⼦树,然后处理右⼦树,最后再处理当前节点。在处理当前节点时,我们可以判断其是否为叶⼦节点且其值是否为0。如果满⾜条件,我们可以删除当前节点。
• 需要注意的是,在删除叶⼦节点时,其⽗节点很可能会成为新的叶⼦节点。因此,在处理完⼦节点后,我们仍然需要处理当前节点。这也是为什么选择后序遍历的原因(后序遍历⾸先遍历到的⼀定是叶⼦节点)。
• 通过使⽤后序遍历,我们可以逐步删除叶⼦节点,并且保证删除后的节点仍然满⾜删除操作的要
求。这样,我们可以较为⽅便地实现删除操作,⽽不会影响最终的结果。
• 若在处理结束后所有叶⼦节点的值均为1,则所有⼦树均包含1,此时可以返回。

递归函数头设计:void dfs(TreeNode root)
(1)返回值:⽆;
(2)参数:当前需要处理的节点;
(3)函数作⽤:判断当前节点是否需要删除,若需要删除,则删除当前节点。

后序遍历的主要流程(函数体):

(1)递归出⼝:当传⼊节点为空时,不做任何处理;
(2)递归处理左⼦树;
(3)递归处理右⼦树;
(4)处理当前节点:判断该节点是否为叶⼦节点(即左右⼦节点均被删除,当前节点成为叶⼦节点),并且节点的值为0:

  • 如果是,就删除掉;
  • 如果不是,就不做任何处理。
    public TreeNode pruneTree(TreeNode root) {//递归出口if(root == null){return null;}//判断左子树root.left = pruneTree(root.left);//判断右子树root.right = pruneTree(root.right);//判断if(root.left == null && root.right == null && root.val == 0){root = null;}return root;}

4. 验证二叉搜索树

力扣题目链接

 

解析:

中序遍历按照左⼦树、根节点、右⼦树的顺序遍历⼆叉树的所有节点,通常⽤于⼆叉搜索树相关题
⽬。
(1)如果⼀棵树是⼆叉搜索树,那么它的中序遍历的结果⼀定是⼀个严格递增的序列。
(2)因此,我们可以初始化⼀个⽆穷⼩的全区变量,⽤来记录中序遍历过程中的前驱结点。那么就可以在中序遍历的过程中,先判断是否和前驱结点构成递增序列,然后修改前驱结点为当前结点,传⼊下⼀层的递归中。 

算法流程:
(1)初始化⼀个全局的变量prev,⽤来记录中序遍历过程中的前驱结点的val;
(2)中序遍历的递归函数中:
① 设置递归出⼝:root == null 的时候,返回true;(叶子结点本身就是一棵二叉搜索树)
② 先递归判断左⼦树是否是⼆叉搜索树,⽤left标记;
③ 然后判断当前结点是否满⾜⼆叉搜索树的性质;
▪ 如果当前结点的val⼤于prev,说明满⾜条件,将prev改为root.val;
▪ 如果当前结点的val⼩于等于prev,说明不满⾜条件,return false;
最后递归判断右⼦树是否是⼆叉搜索树,⽤right标记;
(3)只有当left和right都是true的时候,才返回true。

    long prev = Long.MIN_VALUE;//存放上一个结点的值public boolean isValidBST(TreeNode root) {if(root == null)return true;//递归判断左子树boolean left = isValidBST(root.left);if(prev < root.val)prev = root.val;//判断当前节点是否为二叉搜索树,右边就不需要搞elsereturn false;//剪枝,剪掉右子树的判断if(left == false)return false;//递归判断右子树,告诉父节点,我不是二叉搜索树,你也不是boolean right = isValidBST(root.right);if(left == true && right == true)return true;elsereturn false;}

 

5. 二叉搜索树中第k小的元素

力扣题目链接 

 

解析:

中序遍历 + 计数器剪枝

我们可以根据中序遍历的过程,只需扫描前k个结点即可。
因此,我们可以创建⼀个全局的计数器count,将其初始化为k,每遍历⼀个节点就将count--。直到某次递归的时候,count的值等于1,说明此时的结点就是我们要找的结果。

    int ret = 0;//用来存储最终结果int count;//用来表示要找第几个结点public int kthSmallest(TreeNode root, int k) {count = k;dfs(root);return ret;}public void dfs(TreeNode root){if(root == null)return;dfs(root.left);count--;if(count == 0){ret = root.val;}dfs(root.right);}

 

6. 二叉树的所有路径

力扣题目链接 

 

解析:

使⽤深度优先遍历(DFS)求解。
路径以字符串形式存储,从根节点开始遍历,每次遍历时将当前节点的值加⼊到路径中,如果该节点为叶⼦节点,将路径存储到结果中。否则,将"->"加⼊到路径中并递归遍历该节点的左右⼦树。
定义⼀个结果数组,进⾏递归。递归具体实现⽅法如下:
(1)如果当前节点不为空,就将当前节点的值加⼊路径path中,否则直接返回;
(2)判断当前节点是否为叶⼦节点,如果是,则将当前路径加⼊到所有路径的存储数组paths中;
(3)否则,将当前节点值加上"->"作为路径的分隔符,继续递归遍历当前节点的左右⼦节点;
(4)返回结果数组;


注:特别地,我们可以只使⽤⼀个字符串存储每个状态的字符串,在递归回溯的过程中,需要将路径中的当前节点移除,以回到上⼀个节点。

具体实现⽅法如下:
(1)定义⼀个结果数组和⼀个路径数组。
(2)从根节点开始递归,递归函数的参数为当前节点、结果数组和路径数组。
① 如果当前节点为空,返回。
② 将当前节点的值加⼊到路径数组中。
③ 如果当前节点为叶⼦节点,将路径数组中的所有元素拼接成字符串,并将该字符串存储到结果
数组中。
④ 递归遍历当前节点的左⼦树。
⑤ 递归遍历当前节点的右⼦树。
⑥ 回溯,将路径数组中的最后⼀个元素移除,以返回到上⼀个节点。

    List<String> ret;public List<String> binaryTreePaths(TreeNode root) {ret = new ArrayList<>();dfs(root,new StringBuffer());return ret;}public void dfs(TreeNode root,StringBuffer curPath){//起恢复现场的作用StringBuffer path = new StringBuffer(curPath);path.append(Integer.toString(root.val));if(root.left == null && root.right == null){ret.add(path.toString());return;}path.append("->");if(root.left != null) dfs(root.left,path);if(root.right !=null) dfs(root.right,path);}

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

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

相关文章

全国性教师教学竞赛目录

全国性教师教学竞赛目录 序号 比赛名称 主办单位 开始年份 学科分类 拟举办时间 1 全国高校教师教学创新大赛 中国高等教育学会 2020 综合类 7-9月 2 全国高校青年教师教学竞赛 中国教科文卫体工会全国委员会 2012 综合类 8-10月 3 全国高校辅导员素质能力大赛 教育部思想政治…

【DP】1312.让字符串成为回文串的最少插入次数

题目 法1&#xff1a;DP 基本方法&#xff0c;必须掌握&#xff01; class Solution {public int minInsertions(String s) {int n s.length();if (n < 2) {return 0;}int[][] dp new int[n][n]; // 对于字符串s[i:j], 至少需要dp[i][j]次操作构成回文串for (int i n …

实验七 RMAN恢复管理器

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…

利用appium自动控制移动设备并提取数据

安装appium-python-client模块并启动已安装好的环境 安装appium-python-client模块 在window的虚拟环境下执行pip install appium-python-client 启动夜神模拟器&#xff0c;进入夜神模拟器所在的安装路径的bin目录下&#xff0c;进入cmd终端&#xff0c;使用adb命令建立adb…

Kubernetes (十五) 认证与授权

一. 访问控制 二.UserAccount与serviceaccount区别 将认证信息添加…

关闭Windows自动更新的6种方法

关闭Windows自动更新的6种方法&#xff01; 方法一&#xff1a;通过Windows设置关闭Windows自动更新 步骤1. 按WinI打开Windows设置页面。步骤2. 单击“更新和安全”>“Windows更新”&#xff0c;然后在右侧详情页中选择“暂停更新7天”选项即可在此后7天内关闭Windows更新…

【Linux】相关背景及环境搭建

前言&#xff1a; 认识 Linux, 了解 Linux 的相关背景&#xff0c;学会如何使用云服务器&#xff0c;掌握使用远程终端工具 xshell 登陆 Linux 服务器 文章目录 一、Linux介绍1.1 关于UNIX1.2 Linux的诞生及发展历程1.3 Linux开源1.4 Linux在各个行业的现状1.5 发行版本 二、Li…

LeetCode 热题 100 | 双指针(上)

目录 1 283. 移动零 2 11. 盛最多水的容器 3 15. 三数之和 菜鸟做题第一周&#xff0c;语言是 C 1 283. 移动零 解题思路&#xff1a; 两个指针一前一后遍历数组前者永远指向 0&#xff0c;后者永远在寻找非 0 数的路上后者找到一个非 0 数就和前者进行一个数值交换 …

x-cmd pkg | jieba - 中文结巴分词工具

目录 简介首次用户功能特点竞品分析进一步阅读 简介 结巴中文分词&#xff08;jieba&#xff09;是一款在 Python 环境下使用的开源中文文本分词工具。它支持多种分词模式&#xff0c;包括精确模式、全模式和搜索引擎模式&#xff0c;同时支持繁体分词和自定义词典。因其易用性…

代码随想录算法训练营第十一天|20 有效的括号、1047 删除字符串中的所有相邻重复项、150 逆波兰表达式求值

20 有效的括号 题目链接&#xff1a;有效的括号 思路 由于栈结构的特殊性&#xff0c;非常适合做对称匹配类的题目。按照逻辑来讲&#xff0c;首先我们要确定下有几种不匹配的情况&#xff1a; 多了左括号多了右括号左右括号不匹配 然后左括号和右括号是一对一的&#xff…

[202401C]巨人之力的题解

原题描述&#xff1a; 时间限制: 1000ms 空间限制: 262144kb 题目描述 两千多年以前&#xff0c;身为艾尔迪亚人的尤弥尔意外获得巨人之力&#xff0c;并且创造了九大巨人&#xff0c;其无以匹敌的力量使得整个世界都陷入了无尽的战乱纷争&#xff0c;艾尔迪亚之外的人类过…

使用excel从1-2048中随机选择1个整数,并展示与其对应的单词

在Excel中&#xff0c;你可以使用以下指令来从1到2048之间随机选择一个整数&#xff0c;并展示其对应的单词&#xff1a; 1. 首先&#xff0c;在一个空白单元格中输入以下公式&#xff1a; INDEX(单词列表范围, RANDBETWEEN(1, 2048)) 这里的"单词列表范围"是一个包…

Vue中对前端按钮高亮的几种操作方式

在Vue中我们有时候会碰到一些需求&#xff0c;就是在点击某个按钮的时候&#xff0c;我们执行别的操作&#xff0c;但是我们希望点击别的操作的时候&#xff0c;让我们之前点击的按钮进行处于高亮状态。 个人在处理业务的时候&#xff0c;测试说&#xff0c;当用户选择某个按钮…

thinkphp6 模糊查找json下的字段值

写法&#xff1a; where(json的字段->json下的字段) sql生成json_extract(json的字段&#xff0c;$.json下的字段1.json下的字段2) 可以加上like where(‘‘json的字段->json下的字段, ‘like’, ‘%’. keyword .’%’) sql生成json_extract(json的字段&#xff0c;$.js…

Redis面试题15

Redis 集群的数据一致性如何保证&#xff1f; 答&#xff1a;Redis 集群的数据一致性是通过内置的集群协议和数据迁移机制来保证的。 在 Redis 集群中&#xff0c;数据被分为多个槽&#xff08;slot&#xff09;&#xff0c;每个槽对应一个节点。当一个节点接收到写操作时&…

[GN] Vue3.2 快速上手 ---- 核心语法2

文章目录 标签的 ref 属性props生命周期自定义hook总结 标签的 ref 属性 用在普通DOM标签上&#xff0c;获取的是DOM节点。用在组件标签上&#xff0c;获取的是组件实例对象。 用在普通DOM标签上&#xff1a; <template><div class"person"><h3 ref&…

docker-compose直接官网二进制文件安装(linux)

试了很多种方法&#xff0c;国内镜像库安装&#xff0c;会报语法错误 直接用github官网&#xff0c;命令安装下载太慢&#xff0c;还不如魔法下载 官网链接docker-compose-linux-x86_64https://github.com/docker/compose/releases/download/v2.24.1/docker-compose-linux-x8…

温度采样【通道选通】S9KEAZ128的PTA2和PTA3引脚无法拉高

1、问题记录&#xff1a;由18串温度采样修改成32串温度采样&#xff0c;增加一路adc采样&#xff0c;通过cd4051控制通道选通&#xff0c;代码中增加了相应的代码&#xff0c;发现增加的最后8路温度不能够控制&#xff0c;以24串为例&#xff0c;给温度传感器增加温度&#xff…

pytorch(四)、完整小网络的搭建和sequential的使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言搭建小网络和sequential的使用一、 第一种形式如下&#xff1a;二、第二种方式&#xff0c;使用sequential 前言 至此&#xff0c;神经网络的基础部分就基本结…

解决Spring Boot跨域问题(配置JAVA类)

什么是跨域问题 跨域问题指的是不同端口之间&#xff0c;使用 ajax 无法相互调用的问题。跨域问题本质是浏览器的一种保护机制&#xff0c;它是为了保证用户的安全&#xff0c;防止恶意网站窃取数据。 比如前端用的端口号为8081&#xff0c;后端用的端口号为8080&#xff0c;后…