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

news2024/9/27 5:51:39

在这里插入图片描述

文章目录

  • 前言
  • 什么是二叉树剪枝
  • 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的拼接需要重新创建对象,速度比较慢,所以我们字符串拼接就使用StringBuilder
        dfs(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.coloradmin.cn/o/1309971.html

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

相关文章

mybatis高级扩展-批量映射优化-mappers标签中使用package批量注册的前提条件

1、建库建表 create database mybatis-example; use mybatis-example; create table emp (empNo varchar(40),empName varchar(100),sal int,deptno varchar(10) ); insert into emp values(e001,张三,8000,d001); insert into emp values(e002,李四,9000,d001); insert into…

模型部署系列:10x速度提升,Yolov8检测模型稀疏化——CPU上超500FPS

YOLOv8由广受欢迎的YOLOv3和YOLOv5模型的作者 Ultralytics 开发&#xff0c;凭借其无锚设计将目标检测提升到了一个新的水平。YOLOv8 专为实际部署而设计&#xff0c;重点关注速度、延迟和经济性。 [1] 详细内容请参阅 MarkAI Blog [2] 更多资料及工程项目请关注 MarkAI Githu…

RISCV中的寄存器操作

控制状态寄存器指令 (csrrc、csrrs、csrrw、csrrci、csrrsi、csrrwi)&#xff0c; 使我们可以轻松地访问一些程序性能计数器。对于这些 64 位计数器, 我们一次可以读取 32 位。这些计数器包括了系统时间, 时钟周期以及执行的指令数目。 CSRRW 先读取寄存器的值&#xff1a;tCS…

使用开源技术快速上手 Web 前端开发(内含PPT课件)

11月29日 OpenTiny 参与了华为云开源针对的高校学生在中国人民大学举办的 meetup 交流活动&#xff0c;本次活动演讲主要围绕数据库、Web3、AI大模型、微服务治理、前端等领域展开讨论。OpenTiny 主要为大家分享了 《使用开源技术快速上手Web前端开发》 ,与大家共同探讨前端开发…

AMEYA360 | 太阳诱电汽车电子解决方案

据AMEYA360了解&#xff0c;日前&#xff0c;太阳诱电汽车电子产品相关内容在电子发烧友网站以专题页形式更新。主要内容包含太阳诱电车规级电子元器件产品优势、汽车电子领域应用场景、产品阵容等。 太阳诱电车规级电子元器件以丰富多样的产品阵容助力汽车电子化和智能化。 太…

致远互联-OA wpsAssistServlet 任意文件读取漏洞复现

0x01 产品简介 致远互联-OA 是数字化构建企业数字化协同运营中台,面向企业各种业务场景提供一站式大数据分析解决方案的协同办公软件。 0x02 漏洞概述 致远互联-OA wpsAssistServlet 存在任意文件读取漏洞,攻击者可读取系统密码等敏感信息进一步控制系统。 0x03 复现环境…

第二证券:防御性板块逆势活跃 A股结构性机会轮动

昨日商场慎重张望心境升温&#xff0c;个股跌多涨少。防御性板块中的医药、燃气板块涨幅居前。医药板块中&#xff0c;拓新药业、森萱医药涨超19%&#xff0c;百利天恒、亨迪药业、新赣江等多股涨超10%。 据中国气候网消息&#xff0c;从12月12日夜间初步&#xff0c;新一轮寒…

自定义Axure元件库及原型图泳道图的绘制(详细不同类的案例)

目录 前言 一.自定义元件库 1.1 自定义元件库的作用 1.2 自定义元件的操作 二.流程图 2.1 流程图的作用 2.2 绘制流程图 2.3 简易流程图案例 三.泳道图 3.1 泳道图的作用 3.2 流程图和泳道图的区别 3.3 绘制泳道图 四.绘制前的准备 五.案例 4.1 门诊模块案例 4.2 …

Java面向对象(高级)-- 包装类(wrapper)的使用

文章目录 一、概念&#xff08;1&#xff09;为什么需要包装类&#xff08;2&#xff09; 有哪些包装类&#xff08;3&#xff09;总结 二、包装类&#xff08;1&#xff09;自定义包装类&#xff08;2&#xff09; 包装类与基本数据类型间的转换2.1 为什么需要转换2.2 装箱2.2…

wvp-GB28181-pro 2.0+ZLMediaKit 使用Dockerfile制作镜像以及部署【CentOS7】

说明 部署gb28181和zlm主要需要构建两个镜像&#xff0c;第一个为基础镜像&#xff0c;以centos7为基础构建新的基础镜像base.Dockerfile,第二个镜像为服务部署镜像server.Dockerfile&#xff0c;以第一个镜像base.Dockerfile构建出的镜像为基础镜像进行构建 整个基础镜像的构…

防火墙无非就这8种类型,小白完全不用怕!

你们好&#xff0c;我的网工朋友。 当我们谈到网络开放性带来的安全挑战时&#xff0c;都会想起黑客、病毒、恶意软件等等。 而正是因为这些威胁&#xff0c;让网络安全变成了网络世界里的重要议题&#xff0c;如果说起怎么保护网络安全&#xff0c;基本上我们都会第一时间想…

Java8新特性:函数式(Functional)接口

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

Spark环境搭建和使用方法

目录 一、安装Spark &#xff08;一&#xff09;基础环境 &#xff08;二&#xff09;安装Python3版本 &#xff08;三&#xff09;下载安装Spark &#xff08;四&#xff09;配置相关文件 二、在pyspark中运行代码 &#xff08;一&#xff09;pyspark命令 &#xff08…

HTTP 403错误:禁止访问,如何解除

“HTTP 403错误&#xff0c;禁止访问&#xff01;”这句话是不是听起来就像是在告诉你&#xff1a;“嘿&#xff0c;你没有权限进这个房间&#xff01;”没错&#xff0c;这就是你尝试访问某个网站或资源时可能会遇到的问题。别急&#xff0c;这里有一份秘籍&#xff0c;教你如…

Cobalt Strike四种提权方法

简介 Cobalt Strike是一款基于java的渗透测试神器&#xff0c;常被业界人称为CS神器。自3.0以后已经不在使用Metasploit框架而作为一个独立的平台使用&#xff0c;分为客户端与服务端&#xff0c;服务端是一个&#xff0c;客户端可以有多个&#xff0c;非常适合团队协同作战&a…

剧本杀小程序成为创业者新选择,剧本杀小程序开发

剧本杀作为现下年轻人最喜欢的新兴行业&#xff0c;发展前景非常乐观&#xff0c;即使剧本杀目前处于创新发展阶段&#xff0c;但剧本杀行业依然在快速发展中。 根据业内数据&#xff0c;预计2025年剧本杀市场规模能达到四百多亿元。市场规模的扩大自然也吸引来了不少的创业者…

最前端|Locofy试用报告:设计稿直接转换为代码

目录 一、调研目的 二、调研报告设计 三、调研报告 &#xff08;一&#xff09;操作步骤 &#xff08;二&#xff09;结果初见 (三&#xff09;初步结论 四、总结 五、补充 一、调研目的 初步调研的目标&#xff1a; locofy 的 实操流程locofy 涉及到的相关工作角色及其…

TOUGH系列软件实践技术应用

TOUGH系列软件是由美国劳伦斯伯克利实验室开发的&#xff0c;旨在解决非饱和带中地下水、热运移的通用模拟软件。和传统地下水模拟软件Feflow和Modflow不同&#xff0c;TOUGH系列软件采用模块化设计和有限积分差网格剖分方法&#xff0c;通过配合不同状态方程&#xff08;EOS模…

el-tree搜索的使用

2023.12.11今天我学习了如何对el-tree进行搜索的功能&#xff0c;效果如下&#xff1a; 代码如下&#xff1a; 重点部分&#xff1a;给el-tree设置ref&#xff0c;通过监听roleName的变化过滤数据。 default-expand-all可以设置默认展开全部子节点。 check可以拿到当前节点的…

程序员必读:Python 中如何完美处理日志记录?

日志记录在软件开发中扮演着至关重要的角色。它不仅可以帮助开发人员跟踪应用程序的状态和行为&#xff0c;还能提供有价值的诊断信息。Python 提供了内置的 logging 模块&#xff0c;为开发者提供了一个强大且灵活的日志记录工具。 日志的重要性 在软件开发中&#xff0c;对…