【算法系列篇】递归、搜索和回溯(三)

在这里插入图片描述

文章目录

  • 前言
  • 什么是二叉树剪枝
  • 1. 二叉树剪枝
    • 1.1 题目要求
    • 1.2 做题思路
    • 1.3 代码实现
  • 2. 验证二叉搜索树
    • 2.1 题目要求
    • 2.2 做题思路
    • 2.3 代码实现
  • 3. 二叉搜索树中第k小的元素
    • 3.1 题目要求
    • 3.2 做题思路
    • 3.3 代码实现
  • 4. 二叉树的所有路径
    • 4.1 题目要求
    • 4.2 做题思路
    • 4.3 代码实现

前言

前面我已经给大家分享了两篇关于递归、搜索和回溯相关的问题,但是前面两篇只涉及到了递归,搜索和回溯基本还没涉及到,大家先别着急,后面的文章会为大家分享关于搜索和回溯相关的知识和题目。今天这篇文章主要涉及到的就是关于在递归过程中的剪枝问题。

什么是二叉树剪枝

二叉树剪枝是指通过剪去二叉树中某些子树来提高其质量的过程。具体来说,二叉树剪枝可以包括以下几种情况:

  1. 剪去二叉树中所有空子树:当二叉树中存在空子树时,这些空子树不会对整个二叉树的性能产生任何影响,因此可以将它们全部剪去。
  2. 剪去二叉树中重复的子树:当二叉树中存在重复的子树时,这些重复的子树会对整个二叉树的性能产生负面影响,因此可以将它们全部剪去。
  3. 剪去二叉树中不必要的子树:当二叉树中存在一些不必要的子树时,这些子树不会对整个二叉树的性能产生任何影响,因此可以将它们全部剪去。

通过二叉树剪枝,可以提高二叉树的性能和效率,使得它更加适合于解决实际问题。

其实二叉树剪枝不困难,只需要我们在递归的过程中做出适当的判断就可以到达剪枝的目的。

1. 二叉树剪枝

https://leetcode.cn/problems/binary-tree-pruning/

1.1 题目要求

给你二叉树的根结点 root ,此外树的每个结点的值要么是 0 ,要么是 1 。

返回移除了所有不包含 1 的子树的原二叉树。

节点 node 的子树为 node 本身加上所有 node 的后代。

示例 1:
在这里插入图片描述

输入:root = [1,null,0,0,1]
输出:[1,null,0,null,1]
解释:
只有红色节点满足条件“所有不包含 1 的子树”。 右图为返回的答案。

示例 2:
在这里插入图片描述

输入:root = [1,0,1,0,0,0,1]
输出:[1,null,1,null,1]

示例 3:
在这里插入图片描述

输入:root = [1,1,0,1,1,0,1,0]
输出:[1,1,0,1,1,null,1]

提示:

树中节点的数目在范围 [1, 200] 内
Node.val 为 0 或 1
/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public TreeNode pruneTree(TreeNode root) {}
}

1.2 做题思路

想要做好递归,我们需要以宏观的视角来解决微观问题。首先先来判断给我们的节点是否是null,如果是则直接返回null,不是,则将根节点的左子树和右子树分别交给函数,通过这个函数,我们不需要知道这个函数的具体细节,我们只需要相信他一定能够帮助我们完成剪枝操作。当根节点的左右子树都完成剪枝操作之后,就进行判断,如果根节点的左右子树都为null,并且根节点的值为0,那么就可以将根节点置为null,然后返回root。

在这里插入图片描述
在这里插入图片描述

1.3 代码实现

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {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) {if (root.val == 0) root = null;}return root;}
}

在这里插入图片描述

2. 验证二叉搜索树

https://leetcode.cn/problems/validate-binary-search-tree/

2.1 题目要求

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

示例 1:
在这里插入图片描述

输入:root = [2,1,3]
输出:true

示例 2:
在这里插入图片描述

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

提示:

树中节点数目范围在[1, 104] 内
-231 <= Node.val <= 231 - 1
/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public boolean isValidBST(TreeNode root) {}
}

2.2 做题思路

我们都知道二叉搜索树是二叉树中的任何一个如果左右孩子存在,那么该节点左孩子节点的值要小于该节点的值,并且该节点的值要小于该节点右孩子节点的值,也就是说:二叉搜索树使用中序遍历的话得到的是一个升序的数字。那么在这道题目中,我们该如何判断某个节点的左孩子节点的值小于该节点的值,右孩子节点的值大于该节点的值呢?

我们可以使用前序遍历的方法,先找到二叉搜索树中最小的节点,然后用 prev 记录这个值,返回的时候,就先判断该节点的左子树是否符合二叉搜索树,如果不符合就可以直接返回 false,如果符合的话就需要将 prev 的值与 root 的 val 进行比较,如果 prev < root.val,那么将 prev 的值替换为当前节点的值,并且继续去判断该节点右子树是否为二叉搜索树。

在这里插入图片描述
在这里插入图片描述

2.3 代码实现

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {long prev = Long.MIN_VALUE;public boolean isValidBST(TreeNode root) {if (root == null) return true;boolean l = isValidBST(root.left);if (l == false) return false;if (root.val > prev) prev = root.val;else return false;boolean r = isValidBST(root.right);return l && r;}
}

在这里插入图片描述

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

https://leetcode.cn/problems/kth-smallest-element-in-a-bst/

3.1 题目要求

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

示例 1:
在这里插入图片描述

输入:root = [3,1,4,null,2], k = 1
输出:1

示例 2:
在这里插入图片描述

输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3

提示:

树中的节点数为 n 。
1 <= k <= n <= 104
0 <= Node.val <= 104
/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public int kthSmallest(TreeNode root, int k) {}
}

3.2 做题思路

这道题目可以使用优先级队列来解决,但是为了加强递归的使用,我们不使用优先级队列,而是使用递归来解决这个问题,根据二叉树的特性,要想找到二叉搜索树中第k小的元素,我们可以使用中序遍历二叉搜索树的方法,并且使用全局变量count 来记录当前遍历的节点是第几小的元素,以及使用一个全局变量 ret 来记录第 k 小的元素,中序遍历,没遍历一个节点,count就–,如果 count 为 0,就说明找到了这个元素。

在递归中,有些情况使用全局变量可以使得我们的代码变得很简单。

3.3 代码实现

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {int count, ret;public int kthSmallest(TreeNode root, int k) {count = k;dfs(root);return ret;}private void dfs(TreeNode root) {if (count == 0 || root == null) return;dfs(root.left);count--;if (count == 0) {ret = root.val;return;}dfs(root.right);}
}

在这里插入图片描述

4. 二叉树的所有路径

https://leetcode.cn/problems/binary-tree-paths/

4.1 题目要求

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

示例 1:
在这里插入图片描述

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

示例 2:

输入:root = [1]
输出:["1"]

提示:

树中节点的数目在范围 [1, 100] 内
-100 <= Node.val <= 100
/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public List<String> binaryTreePaths(TreeNode root) {}
}

4.2 做题思路

在这个题目中,我们可以使用前序遍历的方式,将路径上的所有节点的值给拼接到字符串的后面,当遇到叶子节点的时候就将这个字符串添加到集合中,然后返回,但是在返回的时候呢?我们需要将前面添加的一个节点的值给移除。

在这里插入图片描述
但是还不止如此,看题目我们可以发现,在节点和节点之间还需要使用 -> 来进行连接,所以我们到底什么时候移除 -> 和上一个几点的值,什么时候只是移除节点的值,如果字符串使用的是全局变量的话,回溯(恢复现场)就会比较麻烦,所以这个题目我们可以将字符串作为参数传递给函数,这样当返回的时候,这个参数就会自动回到之前的模样。

4.3 代码实现

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {//全局的集合变量用来存储二叉树所有路径上的值List<String> list;public List<String> binaryTreePaths(TreeNode root) {list = new ArrayList<>();//因为String的拼接需要重新创建对象,速度比较慢,所以我们字符串拼接就使用StringBuilderdfs(root, new StringBuilder());return list;}private void dfs(TreeNode root, StringBuilder s) {if (root == null) return;//因为StringBuilder的变化不会因为函数的返回而恢复,所以这里我们创建一个临时的StringBuidler类StringBuilder sb = new StringBuilder(s);sb.append(root.val);if (root.left == null && root.right == null) {list.add(sb.toString());return;}//如果当前节点不是叶子节点,那么就加上->sb.append("->");dfs(root.left, sb);dfs(root.right, sb);}
}

在这里插入图片描述

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

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

相关文章

(八)数组和函数实践:扫雷游戏

目录 1. 扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 1.2 游戏的分析和设计 1.2.1 数据结构的分析 1.2.2 文件结构设计 2. 扫雷游戏的代码实现 3. 如何生成用户版本 4. 完整的排雷程序 1. 扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 1&#xff09;使用控制台实现经典…

数据结构和算法-栈

数据结构和算法-栈 文章目录 数据结构和算法-栈1. 栈的介绍2. 栈的应用场景3. 栈的快速入门3.1 用数组模拟栈3.2 课堂作业-用链表模拟栈 4. 栈实现综合计算器4.1 课堂作业-加入小括号5. 栈的三种表达式-**前缀、中缀、后缀表达式(逆波兰表达式)**5.1 前缀表达式(波兰表达式)5.1…

中低压MOS 适用于电子烟等产品—— 较小的开关损耗 过流能力好

工作原理&#xff1a; 当用户在吸嘴处抽吸时&#xff0c;气流经过进气孔&#xff0c;穿 过电路板上方的咪头&#xff0c;咪头即产生电信号&#xff0c;驱 动芯片板&#xff0c;让电池供电给雾化芯&#xff0c;雾化芯中的 发热丝将电能转化成热能&#xff0c;当温度达到雾化液…

LeetCode-2487. 从链表中移除节点【栈 递归 链表 单调栈】

LeetCode-2487. 从链表中移除节点【栈 递归 链表 单调栈】 题目描述&#xff1a;解题思路一&#xff1a;可以将链表转为数组&#xff0c;然后从后往前遍历&#xff0c;遇到大于等于当前元素的就入栈&#xff0c;最终栈里面的元素即是最终的答案。解题思路二&#xff1a;递归&am…

【一步到位】汽车过户全攻略:轻松搞定,告别繁琐流程

校长车行是一家昆明二手车代办公司&#xff0c;今天我们要聊一聊一个让很多人头疼的问题——汽车过户。相信很多朋友在购买二手车或者需要将车辆转让给他人时&#xff0c;都会遇到这个繁琐的流程。那么&#xff0c;如何才能轻松搞定汽车过户呢&#xff1f;接下来&#xff0c;就…

(0-1)分布

假设离散型随机变量X只可能取到0、1两个值&#xff0c;它的分布律为&#xff1a; &#xff0c;其中&#xff0c; 那么称X服从参数为p的0-1分布&#xff0c;也叫两点分布。 其实上面公式就是将下面两个式子写在一起&#xff1a;

【Hive_02】查询语法

1、基础语法2、基本查询&#xff08;Select…From&#xff09;2.1 全表和特定列查询2.2 列别名2.3 Limit语句2.4 Where语句2.5 关系运算函数2.6 逻辑运算函数2.7 聚合函数 3、分组3.1 Group By语句3.2 Having语句3.3 Join语句&#xff08;1&#xff09;等值与不等值Join&#x…

SUPER-ADAM: Faster and Universal Framework of Adaptive Gradients

这周看了啥&#xff1a; 本周主要来看看别人是如何证明收敛的&#xff0c;围绕算法SUPER-ADAM 的更新过程和论文后面的证明&#xff0c;&#xff08;这篇证明比上周的亲切多了&#xff0c;我哭死&#xff09;仔细看了证明每一步的推导&#xff08;至于作者如何想出的&#xff…

verilog基础语法之比较器

逻辑运算符以及逻辑电路概述 逻辑运算符常用于条件判断语句&#xff0c;输出为布尔值True/False。逻辑运算符是基于比较器构造的。比较器电路是产生逻辑比较的本质&#xff1b;比较器电路的复杂度与位宽和比较类型相关&#xff1b;一般情况下可以先构造基本比较器&#xff0c;…

原生Html 引入element UI + vue3 表单校验设置

效果&#xff1a; 提交时&#xff0c;检验结果展示 html源码 <!DOCTYPE html> <html> <!--带搜索输入框下拉弹窗 --> <head><meta charset"UTF-8"><!-- import Vue before Element --><script src"../js/vue3.3.8/vu…

jmeter,通过Ant插件生成html报告,展示接口详细信息

一、下载Ant 下载地址&#xff1a;Apache Ant - 二进制发行版 二、安装 1、Ant环境变量 解压Ant目录&#xff1b;配置系统环境变量&#xff0c;添加ANT_PATH&#xff0c;值为D:\Software\Ant_plugIn\apache-ant-1.10.14配置系统环境变量Path&#xff0c;添加Ant路径 %ANT_H…

Unity之OpenXR+XR Interaction Toolkit接入Meta Quest3

前言 随着备受期待的Meta Quest 3与今年10月10日发布,这款来自Meta的下一代VR游戏头戴设备承诺将彻底改变您的游戏方式。 Meta Quest 3,玩家只需轻松一触即可在虚拟现实和真实世界之间无缝切换,无需摘下头戴设备进行快速现实检查。 Meta Quest 3最引人注目的特点之一是其能…

webpack学习-5.代码分离

webpack学习-5.代码分离 1.入口起点2.防止重复2.1 入口依赖2.2 SplitChunksPlugin 3.动态导入3.1 使用符合 ECMAScript 提案 的 import() 语法3.2 使用 webpack 特定的 require.ensure 4.预获取/预加载模块5.分析bundle6.总结 1.入口起点 代码分离是 webpack 中最引人注目的特…

AIGC - 环境搭建

一. 硬件环境 1. 超微7048主板&#xff0c;最多可搭载4块GPU 2. 2个Intel的 Xen至强 14核 CPU 3. 目前安装了一块Nvidia 的P40 GPU&#xff0c;后续根据需要还最多可以扩展3块GPU 4. 4T机械 2T Nvme固态&#xff0c; 5. 4条64G DDR4内存条&#xff0c;共 196G内存…

QT多项目管理

.pro文件配置解释&#xff1a;​​​​​​ Qt 中的多项目管理_qt子目录项目-CSDN博客Qt 模块化开发之 pro 子项目开发_qt 子项目-CSDN博客关于Qt编译库&#xff08;1&#xff09;&#xff1a;在子项目中编译动态库并且使用_qt编译动态库后配置qt-CSDN博客QT release下的编译…

涵盖多种功能,龙讯旷腾Module第六期:输运性质

Module是什么 在PWmat的基础功能上&#xff0c;我们针对用户的使用需求开发了一些顶层模块&#xff08;Module&#xff09;。这些Module中的一部分是与已有的优秀工具的接口&#xff0c;一部分是以PWmat的计算结果为基础得到实际需要的物理量&#xff0c;一部分则是为特定的计…

排序算法(二)-冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、基数排序

排序算法(二) 前面介绍了排序算法的时间复杂度和空间复杂数据结构与算法—排序算法&#xff08;一&#xff09;时间复杂度和空间复杂度介绍-CSDN博客&#xff0c;这次介绍各种排序算法——冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、基数排序。 文章目录 排…

排序-归并排序与计数排序

文章目录 一、归并排序1、概念2、过程3、代码实现4、复杂度5、稳定性 二、 计数排序1、思路2、代码实现3、复杂度&#xff1a;4、稳定性 一、归并排序 1、概念 是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已…

关键点检测☞png格式换bmp,且labelme标注的json中imagePath同步修改格式

import os import cv2 import jsondef bmp2jpg(in_img_path, out_dir_name): # .png -> .bmp# img = cv2.imread(in_img_path) # 彩色图片,位深24img =</

GDPU 数据结构 天码行空13

文章目录 一、【实验目的】二、【实验内容】三、实验源代码四、实验结果五、实验总结 一、【实验目的】 (1) 理解插入排序算法的实现过程&#xff1b; &#xff08;2&#xff09;理解不同排序算法的时间复杂度及适用环境&#xff1b; &#xff08;3&#xff09;了解算法性能…