Java LeetCode篇-二叉搜索树经典解法(实现:二叉搜索树的最近公共祖先、根据前序遍历建树等)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
  

文章目录

        1.0 判断合法

        1.1 使用遍历方式实现验证二叉搜索树

        1.2 使用递归方式实现验证二叉搜索树

        2.0 求范围和

        2.1 使用非递归实现二叉搜索树的范围和

        2.2 使用递归方式实现二叉搜索树的范围和

        3.0 根据前序遍历结果建树

        3.1 使用非递归实现前序遍历构造二叉搜索树

        3.2 使用递归实现前序遍历构造二叉搜索树

        4.0 二叉搜索树的最近祖先

        4.1 使用遍历方式实现二叉搜索树的最近公共祖先

        5.0 本篇二叉搜索树实现 LeetCode 经典题的完整代码


        1.0 判断合法

题目:

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

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

        节点的左子树只包含 小于 当前节点的数。

        节点的右子树只包含 大于 当前节点的数。

        所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

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

OJ链接:98. 验证二叉搜索树

        1.1 使用遍历方式实现验证二叉搜索树

        具体思路为:利用中序遍历的效果,若每一个节点的值都比前一个节点的值大,则符合二叉搜索树;若出现某一个节点或者多个节点的值比前一个节点的值大,则符合二叉搜索树。

代码如下:

    //使用遍历实现验证二叉树
    public boolean isValidBST2(TreeNode node) {
        Stack<TreeNode> stack = new Stack<>();
        TreeNode p = node;
        long prev = Long.MIN_VALUE;
        while (p != null || !stack.isEmpty()) {
            if (p != null) {
                stack.push(p);
                p = p.left;
            }else {
                TreeNode pop = stack.pop();
                if(pop.val <= prev) {
                    return false;
                }
                prev = pop.val;
                p = pop.right;
            }
        }
        return true;
    }

        需要注意的是,当前节点的值等于前一个节点的值时,同样是不属于二叉搜索树。

        1.2 使用递归方式实现验证二叉搜索树

        具体思路为:利用递归遍历该二叉树时,先对节点的左子树进行操作,若该左子树返回的是 true 时,则继续判断当前节点的值 val ;若该左子树返回的是 false 时,则不需要再进行下去了,返回 false 结束。若当前当前节点的值小于前一个节点的值,则返回  false ;若当前节点的值大于前一个节点时,需要将 prev = node.val 赋值完后,继续判断下去。直到遇到 node == null 时,返回 true 。若左子树与当前的节点都为 true 时,接着到该节点的右子树。最后当且仅当,左右子树都为 true 时,说明该二叉树是属于二叉搜索树

代码如下:

      //使用递归实现验证二叉树
    private long prev = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if(root == null) {
            return true;
        }
        boolean l = isValidBST(root.left);
        if (!l) {
            return false;
        }
        if(prev >= root.val) {
            return false;
        }
        prev = root.val;
        return isValidBST(root.right);
    }

        2.0 求范围和

题目:        

        给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和。

示例 1:

输入:root = [10,5,15,3,7,null,18], low = 7, high = 15
输出:32

OJ链接:938. 二叉搜索树的范围和

        2.1 使用非递归实现二叉搜索树的范围和

        具体思路为:利用中序遍历效果,对于满足 node.val > slow && node.val  < high 的节点 node 将该节点的 node.val 累加到 sum 中,直到遇到 node.val > high 时,则直接返回 sum 结果即可。

代码如下:

    //使用非递归求二叉搜索树的范围和
    public int rangeSum2(TreeNode root,int slow,int high) {
        Stack<TreeNode> stack = new Stack<>();
        TreeNode p = root;
        int sum = 0;
        while(p != null || !stack.isEmpty()) {
            if(p != null) {
                stack.push(p);
                p = p.left;
            }else {
                TreeNode pop = stack.pop();
                if(pop.val > high) {
                    break;
                }
                if(pop.val >= slow) {
                    sum += pop.val;
                }
                p = pop.right;
            }
        }
        return sum;
    }

        2.2 使用递归方式实现二叉搜索树的范围和

        具体思路为:首先考虑符合 slow 与 high 范围之内的节点值,需要返回当前节点的值与该节点的左子树与右子树的符合范围的节点值。再来考虑不符合 slow 与 high 范围之内的节点值时,当 node.val < slow ,则不能再往该节点的左子树继续递归下去了,需要往该节点的右子树递归下去;当 node.val > slow ,则不能往该节点的右子树继续递归下去了,需要往该节点的左子树递归寻找符合范围值的节点。

代码如下:

    //使用递归求二叉搜索树的范围和
    public int rangeSum(TreeNode root,int slow, int high) {
        if(root == null) {
            return 0;
        }
        if(root.val < slow) {
            return rangeSum(root.right,slow,high);
        }
        if(root.val > high) {
            return rangeSum(root.left,slow,high);
        }
        return root.val + rangeSum(root.left,slow,high) + 
                          rangeSum(root.right,slow,high);
    }

        3.0 根据前序遍历结果建树

题目:

        给定一个整数数组,它表示BST(即 二叉搜索树 )的 先序遍历 ,构造树并返回其根。

保证 对于给定的测试用例,总是有可能找到具有给定需求的二叉搜索树。

二叉搜索树 是一棵二叉树,其中每个节点, Node.left 的任何后代的值 严格小于 Node.val , Node.right 的任何后代的值 严格大于 Node.val

二叉树的 前序遍历 首先显示节点的值,然后遍历Node.left,最后遍历Node.right

示例 1:

输入:preorder = [8,5,1,7,10,12]
输出:[8,5,10,1,7,null,12]

OJ链接:1008. 前序遍历构造二叉搜索树

         3.1 使用非递归实现前序遍历构造二叉搜索树

        具体思路为:利用数组中第一个值作为根节点的值,再遍历数组从索引 1 开始直到该数组长度 - 1 。得到每一个数组的值来创建一个新的节点,再自定义 insert 方法将该节点插入二叉搜索树中。关键的是:使用非递归方式实现该方法,首先定义一个 parent 变量,用来记录 p 的父亲节点,循环遍历 p ,若 p.val > node.val 时,先记录 parent = p,再 p = p.left ;若 p.val < node.val 时, 先记录 parent = p,再 p = p.right 。直到 p == null 时,跳出循环,则当前的 parent 就是该二叉树的叶子节点,在判断 node.valparent.val 的大小关系,若 node.val > parent.val,则 parent.right = node;若 node.val < parent.val,则 parent.left = node

代码如下:

//根据前序遍历的结果建树
    public TreeNode bstFromPreorder(int[] preorder) {
        TreeNode root = new TreeNode(preorder[0]);
        for(int i = 1; i < preorder.length; i++) {
            TreeNode p = new TreeNode(preorder[i]);
            insert(root,p);
        }
        return root;

    }
    //使用非递归的方式
    public void insert(TreeNode root, TreeNode node) {
        TreeNode p = root;
        TreeNode parent = null;
        while(p != null) {
            if(p.val < node.val) {
                parent = p;
                p = p.right;
            }else if(p.val > node.val) {
                parent = p;
                p = p.left;
            }
        }
        if(parent.val > node.val) {
            parent.left = node;
        }else {
            parent.right = node;
        }
    }

        3.2 使用递归实现前序遍历构造二叉搜索树

        具体思路为:递归遍历直到遇到 node == null 时,那么 node = new TreeNode(val) 。若 node.val > val 时,向左子树递归下去 node = node.left;若 node.val < val 时,先右子树递归下去 node = node.right 。每一次递归完,返回的时候,需要重新链接当前节点的左子树或者右子树,再返回当前节点。

代码如下:

//根据前序遍历的结果建树
    public TreeNode bstFromPreorder(int[] preorder) {
        TreeNode root = new TreeNode(preorder[0]);
        for(int i = 1; i < preorder.length; i++) {
            TreeNode p = new TreeNode(preorder[i]);
            insert(root,p);
        }
        return root;

    }
//使用递归的方式
    public  TreeNode insert(TreeNode node, int val) {
        if (node == null) {
            return new TreeNode(val);
        }
        if (node.val > val) {
            node.left = insert(node.left,val);
        }else {
            node.right = insert(node.right,val);
        }
        return node;
    }

        4.0 二叉搜索树的最近祖先

题目:

        给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。

OJ链接:235. 二叉搜索树的最近公共祖先

        4.1 使用遍历方式实现二叉搜索树的最近公共祖先

        具体思路为:若 p 与 q 在当前节点的左右子树,那么该节点就是该 q 与 p 的公共最近的祖先;若 p 与 q 在当前节点的同一侧(都在该当前节点的左子树或者右子树),则需要继续往下遍历,当 node.val < p.val && node.val < q.val 或者 node.val > p.val && node.val > q.val 都需要继续遍历,直到跳出循环后,则当前节点 node 就是该 p 与 q 的公共最近节点。

代码如下:

//二叉搜索树的最近祖宗
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        TreeNode a = root;
        while(p.val < a.val && q.val < a.val || p.val > a.val && q.val > a.val) {
            if(p.val < a.val) {
                a = a.left;
            }else {
                a = a.right;
            }
        }
        return a;
    }

        5.0 本篇二叉搜索树实现 LeetCode 经典题的完整代码

import java.util.Stack;

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;
      }

      //使用递归实现验证二叉树
    private long prev = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if(root == null) {
            return true;
        }
        boolean l = isValidBST(root.left);
        if (!l) {
            return false;
        }
        if(prev >= root.val) {
            return false;
        }
        prev = root.val;
        return isValidBST(root.right);
    }

    //使用遍历实现验证二叉树
    public boolean isValidBST2(TreeNode node) {
        Stack<TreeNode> stack = new Stack<>();
        TreeNode p = node;
        long prev = Long.MIN_VALUE;
        while (p != null || !stack.isEmpty()) {
            if (p != null) {
                stack.push(p);
                p = p.left;
            }else {
                TreeNode pop = stack.pop();
                if(pop.val <= prev) {
                    return false;
                }
                prev = pop.val;
                p = pop.right;
            }
        }
        return true;
    }

    //使用递归求二叉搜索树的范围和
    public int rangeSum(TreeNode root,int slow, int high) {
        if(root == null) {
            return 0;
        }
        if(root.val < slow) {
            return rangeSum(root.right,slow,high);
        }
        if(root.val > high) {
            return rangeSum(root.left,slow,high);
        }
        return root.val + rangeSum(root.left,slow,high) + rangeSum(root.right,slow,high);
    }

    //使用非递归求二叉搜索树的范围和
    public int rangeSum2(TreeNode root,int slow,int high) {
        Stack<TreeNode> stack = new Stack<>();
        TreeNode p = root;
        int sum = 0;
        while(p != null || !stack.isEmpty()) {
            if(p != null) {
                stack.push(p);
                p = p.left;
            }else {
                TreeNode pop = stack.pop();
                if(pop.val > high) {
                    break;
                }
                if(pop.val >= slow) {
                    sum += pop.val;
                }
                p = pop.right;
            }
        }
        return sum;
    }

    //根据前序遍历的结果建树
    public TreeNode bstFromPreorder(int[] preorder) {
        TreeNode root = new TreeNode(preorder[0]);
        for(int i = 1; i < preorder.length; i++) {
            TreeNode p = new TreeNode(preorder[i]);
            insert(root,p);
        }
        return root;

    }
    //使用非递归的方式
    public void insert(TreeNode root, TreeNode node) {
        TreeNode p = root;
        TreeNode parent = null;
        while(p != null) {
            if(p.val < node.val) {
                parent = p;
                p = p.right;
            }else if(p.val > node.val) {
                parent = p;
                p = p.left;
            }
        }
        if(parent.val > node.val) {
            parent.left = node;
        }else {
            parent.right = node;
        }
    }
    //使用递归的方式
    public  TreeNode insert(TreeNode node, int val) {
        if (node == null) {
            return new TreeNode(val);
        }
        if (node.val > val) {
            node.left = insert(node.left,val);
        }else {
            node.right = insert(node.right,val);
        }
        return node;
    }

    //二叉搜索树的最近祖宗
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        TreeNode a = root;
        while(p.val < a.val && q.val < a.val || p.val > a.val && q.val > a.val) {
            if(p.val < a.val) {
                a = a.left;
            }else {
                a = a.right;
            }
        }
        return a;
    }

}

        

        本篇为相关二叉搜索树对于 LeetCode 题目的相关解法,希望对你有所帮助。

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

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

相关文章

软件测试|测试平台开发-Flask 入门:URL组成部分详解

简介 Flask 是一款流行的 Python Web 框架&#xff0c;它简单轻量而灵活&#xff0c;适用于构建各种规模的 Web 应用程序。在 Flask 中&#xff0c;URL&#xff08;Uniform Resource Locator&#xff09;是指定 Web 应用程序中资源的唯一标识符。URL 组成部分是构成一个完整 U…

可在图像中生成任意精准文本,支持中文!阿里开源AnyText

随着Midjourney、Stable Difusion等产品的出现&#xff0c;文生图像领域获得了巨大突破。但是想在图像中生成/嵌入精准的文本却比较困难。 经常会出现模糊、莫名其妙或错误的文本&#xff0c;尤其是对中文支持非常差&#xff0c;例如&#xff0c;生成一张印有“2024龙年吉祥”…

在甲骨文云上用 Ray +Vllm 部署 Mixtral 8*7B 模型

在甲骨文云上用 Ray Vllm 部署 Mixtral 8*7B 模型 0. 背景1. 甲骨文云 GPU 实例2. 配置 VCN 的 Security List3. 安装 Ray 和 Vllm4. 启动 Ray5. 启动 Vllm 0. 背景 根据好几个项目的需求&#xff0c;多次尝试 Mixtral-8x7B-Instruct-v0.1 这个模型&#xff0c;确实性能不错。…

GD32移植FreeRTOS

准备工作 GD32开发板。案例是以梁山派为开发板。Windows系统的电脑。当前是以Win11的电脑来实现案例的。Keil开发工具。并且已经安装好GD32依赖环境。FreeRTOS源码包。下载地址为: Releases FreeRTOS/FreeRTOS GitHub 当前以FreeRTOSv202212.01版本为例。也是目前的最新版本…

SpringMVC-HelloWorld

一、SpringMVC简介 1.1 SpringMVC和三层架构 MVC是一种软件架构思想&#xff0c;将软件按照模型、视图和控制器三个部分划分。 M&#xff1a;model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;用于处理数据。JavaBean分为两类&#xff1a; 实体类Bean&…

网络通信(11)-C#TCP服务端封装帮助类实例

本文使用Socket在C#语言环境下完成TCP服务端封装帮助类的实例。 实例完成的功能: 服务器能够连接多个客户端显示在列表中,实现实时刷新。 服务器接收客户端的字符串数据。 选中列表中的客户端发送字符串数据。 在VS中创建C# Winform项目,编辑界面,如下: UI文件 name…

4030 【例题2】Cashier Employment 出纳员问题(Poj1275Hdu1529)————一本通(提高篇)

今天主要来讲讲差分约束 题目大意&#xff1a; 从0点到23点&#xff0c;给出每个时刻需要的售货员个数&#xff0c;再给出每个时刻应征的售货员个数&#xff0c;然后让你求出满足需求的最小售货员个数 解题思路&#xff1a;差分约束 #include <queue> #include <cs…

Spring 动态数据源事务处理

在一般的 Spring 应用中,如果底层数据库访问采用的是 MyBatis,那么在大多数情况下,只使用一个单独的数据源,Spring 的事务管理在大多数情况下都是有效的。然而,在一些复杂的业务场景下,如需要在某一时刻访问不同的数据库,由于 Spring 对于事务管理实现的方式,可能不能达…

已解决 ValueError: Data cardinality is ambiguous 问题

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通Golang》…

WPF 导航界面悬浮两行之间的卡片 漂亮的卡片导航界面 WPF漂亮渐变颜色 WPF漂亮导航头界面 UniformGrid漂亮展现

在现代应用程序设计中&#xff0c;一个漂亮的WPF导航界面不仅为用户提供视觉上的享受&#xff0c;更对提升用户体验、增强功能可发现性和应用整体效率起到至关重要的作用。以下是对WPF漂亮导航界面重要性的详尽介绍&#xff1a; 首先&#xff0c;引人入胜的首页界面是用户与软…

Redis原理篇(Dict的收缩扩容机制和渐进式rehash)

Dict&#xff08;即字典&#xff09; Redis是一种键值型数据库&#xff0c;其中键与值的映射关系就是Dict实现的。 Dict通过三部分组成&#xff1a;哈希表&#xff08;DictHashTable&#xff09;&#xff0c;哈希节点(DictEntry)&#xff0c;字典&#xff08;Dict&#xff09…

【docker】centos7安装harbor

目录 零、前提一、下载离线包二、安装三、访问四、开机自启 零、前提 1.前提是已经安装了docker和docker-compose 一、下载离线包 1. csdn资源&#xff1a;harbor-offline-installer-v2.10.0.tgz 2. 百度云盘&#xff08;提取码&#xff1a;ap3t&#xff09;&#xff1a;harbo…

Nvidia Jetson AGX Orin使用CAN与底盘通信(ROS C++ 驱动)

文章目录 一、Nvidia Jetson AGX Orin使用CAN通信1.1 CAN使能配置修改GPIO口功能1.2 can收发测试 二、通过CAN协议编写CAN的SocketCan ROS1驱动程序2.1 通讯协议2.2 接收数据节点2.3 发送数据节点2.4 功能包配置 三、ROS2驱动程序 一、Nvidia Jetson AGX Orin使用CAN通信 参考…

python股票分析挖掘预测技术指标知识之蜡烛图指标(6)

本人股市多年的老韭菜&#xff0c;各种股票分析书籍&#xff0c;技术指标书籍阅历无数&#xff0c;萌发想法&#xff0c;何不自己开发个股票预测分析软件&#xff0c;选择python因为够强大&#xff0c;它提供了很多高效便捷的数据分析工具包。 我们已经初步的接触与学习其中数…

Java中的String类:深入分析与高级应用

Java中的String类&#xff1a;深入分析与高级应用 1. String类基础1.1 概述1.2 不可变性的好处1.3 字符串常量池 2. 创建String对象3. String类常用方法4. 内存管理4.1 字符串常量池4.2 intern方法 5. String与StringBuilder/StringBuffer6. 性能考虑7. 结论 Java中的String类是…

【Bootstrap学习 day14】

分页 分页是通过将内容分成单独的页面来组织内容的过程&#xff0c;分页导航一般用于文章列表页&#xff0c;下载列表、图片列表等&#xff0c;由于数据很多&#xff0c;不可能在一页显示&#xff0c;一般分页导航包括上一页&#xff0c;下一页、数字页码等。 基础的分页 要创…

【Python机器学习】线性模型——用于二分类的线性模型

线性模型也广泛用于分类问题&#xff0c;对于二分类问题&#xff0c;可以用以下公式进行预测&#xff1a; yw[0]*x[0]w[1]*x[1]…………w[p]*x[p]b>0 公式与现行回归的公式非常类似&#xff0c;但没有返回特征的加权求和&#xff0c;而是为预测设置了阈值。如果函数值小于…

Unity 欧盟UMP用户隐私协议Android接入指南

Unity 欧盟UMP用户协议Android接入指南 官方文档链接开始接入mainTemplate.gradle 中引入CustomUnityPlayerActivity 导入UMP相关的包java类中新增字段初始化UMPSDK方法调用![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d882171b068c46a1b956e80425f3a9cf.png)测…

Linux操作系统基础(06):Linux的文件类型和颜色

1.Linux文件类型 在Linux系统中&#xff0c;文件类型是指文件的种类或类型&#xff0c;它决定了系统对文件的处理方式&#xff0c;文件类型的作用在于告诉系统如何处理文件&#xff0c;不同类型的文件会有不同的默认行为和处理方式&#xff0c;Linux系统中常见的文件类型包括 …

轻松玩转书生·浦语大模型趣味Demo

轻松玩转书生浦语大模型趣味 Demo 轻松玩转书生浦语大模型趣味 Demo 1 大模型及 InternLM 模型简介 1.1 什么是大模型&#xff1f;1.2 InternLM 模型全链条开源 2 InternLM-Chat-7B 智能对话 Demo 2.1 环境准备2.2 模型下载2.3 代码准备2.4 终端运行2.5 web demo 运行 3 Lagen…