二叉搜索树java实现

news2025/1/12 21:50:48

顾名思义,二叉搜索树是一棵二叉树,每个节点就是一个对象,这个对象包含属性left、right和parent。left指向节点的左孩子,right指向节点的右孩子,parent指向节点的父节点(双亲)。如果某个孩子节点和父节点不存在,则相应的属性为null。根节点是树中唯一父节点指针为null的节点。

二叉搜索树中的关键字满足以下性质:

假设x是二叉搜索树中的一个节点,如果y是x的左子树中的一个节点,那么y.key ≤ x.key;如果y是x右子树中的一个节点,那么y.key ≥ x.key。

两棵二叉搜索树的示意图如下:
在这里插入图片描述
二叉搜索树支持查询、求最大值、最小值以及追加和删除等操作,其基于java的简单实现如下:

/**
 * 数据结构-二叉搜索树
 */
public class BinarySearchTree {

    /**
     * 树的根节点
     */
    private Node root;

    /**
     * 根据关键字搜索节点
     *
     * @param key 关键字
     * @return key所在的节点
     */
    public Node search(int key) {
        Node node = root;
        while (node != null && key != node.key) {
            // 如果key小于当前节点的key,则往左孩子节点找,否则往右孩子节点找
            if (key < node.key) {
                node = node.left;
            } else {
                node = node.right;
            }
        }
        return node;
    }

    /**
     * 获取key最小的节点
     *
     * @return key最小的节点
     */
    public Node min() {
        return min(root);
    }

    /**
     * 获取以node为根的子树中key最小的节点
     *
     * @param node 子树根节点
     * @return 子树中key最小的节点
     */
    public Node min(Node node) {
        while (node.left != null) {
            node = node.left;
        }
        return node;
    }

    /**
     * 获取key最大的节点
     *
     * @return key最大的节点
     */
    public Node max() {
        return max(root);
    }

    /**
     * 获取以node为根的子树中key最大的节点
     *
     * @param node 子树根节点
     * @return 子树中key最大的节点
     */
    public Node max(Node node) {
        while (node.right != null) {
            node = node.right;
        }
        return node;
    }

    /**
     * 向树中添加节点
     *
     * @param key 插入节点的key
     */
    public void add(int key) {
        Node node = root;
        Node temp = null;

        // 寻找合适的节点添加新节点
        while (node != null) {
            temp = node;
            if (key < node.key) {
                node = node.left;
            } else {
                node = node.right;
            }
        }
        Node newNode = new Node();
        newNode.parent = temp;
        newNode.key = key;
        if (temp == null) { // 该树上还没有节点
            root = newNode;
        } else if (key < temp.key) {
            temp.left = newNode;
        } else {
            temp.right = newNode;
        }
    }

    /**
     * 删除以key为关键字的节点
     *
     * @param key 关键字
     */
    public void delete(int key) {
        Node node = search(key);
        if (node.left == null) {
            transplant(node, node.right);
        } else if (node.right == null) {
            transplant(node, node.left);
        } else {
            Node min = min(node.right);
            if (!min.parent.equals(node)) {
                transplant(min, min.right);
                min.right = node.right;
                min.right.parent = min;
            } 
            transplant(node, min);
            min.left = node.left;
            min.left.parent = min;
        }
    }

    /**
     * 用node2为跟的子树替换node1为根的子树
     *
     * @param node1 子树1
     * @param node2 子树2
     */
    private void transplant(Node node1, Node node2) {
        // node1是根节点
        if (node1.parent == null) {
            root = node2;
        } else if (node1 == node1.parent.left) { // 如果node1是左孩子
            node1.parent.left = node2;
        } else { // 如果node1是右孩子
            node1.parent.right = node2;
        }
        if (node2 != null) {
            node2.parent = node1.parent;
        }
    }

    public Node getRoot() {
        return root;
    }

    /**
     * 树上的节点
     */
    public static class Node {
        /**
         * 节点关键字
         */
        private int key;

        /**
         * 节点的左孩子
         */
        private Node left;

        /**
         * 节点的右孩子
         */
        private Node right;

        /**
         * 节点的父节点
         */
        private Node parent;

        public int getKey() {
            return key;
        }

        public Node getLeft() {
            return left;
        }

        public Node getRight() {
            return right;
        }

        public Node getParent() {
            return parent;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Node node = (Node) o;
            return key == node.key && Objects.equals(left, node.left)
                    && Objects.equals(right, node.right) && Objects.equals(parent, node.parent);
        }

        @Override
        public int hashCode() {
            return Objects.hash(key, left, right, parent);
        }
    }
}

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

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

相关文章

《安富莱嵌入式周报》第327期:Cortex-A7所有外设单片机玩法LL/HAL库全面上线,分享三款GUI, PX5 RTOS推出网络协议栈,小米Vela开源

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 1、2023 Hackaday大赛胸牌开源 Vectorscope-main.zip (66.83MB) GitHub - Hack-a-Day/Vectorscope: Vectorscope badg…

Android Spannable 使用​注意事项

1、当前示例中间的 "评论"&#xff0c;使用SpannableStringBuilder实现&#xff0c;点击评论会有高亮效果加粗&#xff0c;但再点击其它Bar时无法恢复默认样式。 2、因为SpannableString或SpannableStringBuilder中的效果是叠加的&#xff0c;恢复默认样式需要先移除…

如何用java的虚拟线程连接数据库

我觉得这个很简单 首先确保你idea支持jdk21. 然后把idea编译成的目标字节码设置为21版本的 然后编写代码。 创建虚拟线程的方式有&#xff1a; Runnable runnable () -> {System.out.println("Hello, world!"); };// 创建虚拟线程 Thread virtualThread Thre…

前缀和及差分数组

前缀和 原数组x0x1x2x3x4x5前缀和数组x0x0x1x0x1x2x0x1x2x3x0x1x2x3x4x0x1x2x3x4x5前缀和数组代数形式x0’x1’x2’x3’x4’x5’ 计算原数组某区间的和 sum[x1,x2,x3] 利用前缀和计算 x3-x0 x0x1x2x3-x0 x1x2x3 差分数组 x0x1x2x3x4x5原数组x0x1x2x3x4x5差分数组x0x1-x0x…

使用PySpark 结合Apache SystemDS 进行信号处理分析 (离散傅立叶变换)的简单例子

文章大纲 简介 :什么是 SystemDS ?环境搭建与数据 准备数据预处理模型训练 与 结果评估参考文献简介 :什么是 SystemDS ? SystemDS is an open source ML system for the end-to-end data science lifecycle from data integration, cleaning, and feature engineering, ov…

Android设计模式--模板方法模式

一&#xff0c;定义 定义一个操作中的算法的框架&#xff0c;而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 在面向对象的开发过程中&#xff0c;通常会遇到这样一个问题&#xff0c;我们知道一个算法所需的关键步…

C语言矩阵乘积(ZZULIOJ1127:矩阵乘积)

题目描述 计算两个矩阵A和B的乘积。 输入第一行三个正整数m、p和n&#xff0c;0<m,n,p<10&#xff0c;表示矩阵A是m行p列&#xff0c;矩阵B是p行n列&#xff1b;接下来的m行是矩阵A的内容&#xff0c;每行p个整数&#xff0c;用空格隔开&#xff1b;最后的p行是矩阵B的内…

开发上门送桶装水小程序要考虑哪些业务场景

上门送水业务已经有很长一段时间了&#xff0c;但是最开始都是给用户发名片、贴小广告&#xff0c;然后客户电话订水&#xff0c;水站工作人员再上门去送&#xff0c;这种人工记单和派单效率并不高&#xff0c;并且电话沟通中也比较容易出现偏差&#xff0c;那么根据这个情况就…

在AWS VPC中运行Nagios检查时指定自定义DNS解析器的选项

在AWS VPC中运行Nagios检查&#xff0c;并希望能够指定自定义DNS解析器来处理请求。我想使用Python requests库来实现这个目标。 根据问题描述&#xff0c;您想在AWS VPC中运行Nagios检查&#xff0c;并希望使用Python的requests库来指定自定义DNS解析器。 要解决这个问题&…

C语言——结构体的应用

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 路还在继续&#xff0c;梦还在期…

思维模型 重叠效应

本系列文章 主要是 分享 思维模型 &#xff0c;涉及各个领域&#xff0c;重在提升认知。相似内容易被混淆或遗忘。 1 重叠效应的应用 1.1 重叠效应在教育中的应用 1 通过避免重叠效应提升学习效率 为了避免重叠效应&#xff0c;通过对比、归纳等方法来帮助学生更好地理解和掌…

利用 Apache Ranger 管理 Amazon EMR 中的数据权限

需求背景简介 系统安全通常包括两个核心主题&#xff1a;身份验证和授权。一个解决“用户是谁”的问题&#xff0c;另一个解决“用户允许执行什么操作”的问题。在大数据领域&#xff0c;Apache Ranger 是最受欢迎的授权选择之一&#xff0c;它支持所有主流大数据组件&#xff…

语音识别入门——常用软件及python运用

工具以及使用到的库 ffmpegsoxaudacitypydubscipylibrosapyAudioAnalysisplotly 本文分为两个部分&#xff1a; P1&#xff1a;如何使用ffmpeg和sox处理音频文件 P2&#xff1a;如何编程处理音频文件并执行基本处理 P1 处理语音数据——命令行方式 格式转换 ffmpeg -i video…

黑马React18: Redux

黑马React: Redux Date: November 19, 2023 Sum: Redux基础、Redux工具、调试、美团案例 Redux介绍 Redux 是React最常用的集中状态管理工具&#xff0c;类似于Vue中的Pinia&#xff08;Vuex&#xff09;&#xff0c;可以独立于框架运行 作用&#xff1a;通过集中管理的方式管…

如何使用ArcGIS Pro进行坐标转换

不同来源的数据坐标系可能是不同的&#xff0c;为了统一使用这些数据就需要进行坐标转换&#xff0c;ArcGIS Pro作为专业的GIS软件&#xff0c;坐标转换功能肯定也是包含的&#xff0c;这里为大家介绍一下ArcGIS Pro如何进行坐标转换&#xff0c;希望能对你有所帮助。 数据来源…

shell 脚本循环语句

目录 循环 echo 命令 for 循环次数 for 第二种格式 命令举例 while 脚本举例 双重循环及跳出循环 脚本举例 更改文件和目录的后缀名的脚本 画三角形的脚本 乘法口诀表的脚本 面试例题 补充命令 let 命令 循环 —— 一定要有跳出循环的条件 已知循环的次数 未知…

PS_魔幻

首先打开一个背景图片 然后ctrl j复制一层背景 在调整中将图片改成黑白颜色 点击调整中的 色相/饱和度 调整明度 点击画笔工具&#xff0c;并且设置画笔模板 调节画笔大小&#xff0c;将笔记本电脑涂个概况 然后再新建色相/饱和度 勾选着色 调节背景颜色至喜欢 右键混合选项 …

阿里云99元服务器ECS经济型e实例性能如何?测评来了

阿里云服务器优惠99元一年&#xff0c;配置为云服务器ECS经济型e实例&#xff0c;2核2G配置、3M固定带宽和40G ESSD Entry系统盘&#xff0c;CPU采用Intel Xeon Platinum架构处理器&#xff0c;2.5 GHz主频&#xff0c;3M带宽下载速度384KB/秒&#xff0c;上传速度1028KB/秒&am…

Bean依赖注入注解开发

value Value("xfy")private String userName;private String userName;Value("xiao")public void setUserName(String userName) {this.userName userName;} Autowired // 根据类型进行注入 如果同一类型的Bean有多个&#xff0c;尝试根基名字进行二次…

echarts的横向柱状图文字省略,鼠标移入显示内容 vue3

效果图 文字省略 提示 如果是在x轴上的&#xff0c;就在x轴上添加triggerEvent: true,如果是y轴就在y轴添加&#xff0c;我是在y轴上添加的 并且自定义的方法&#xff08;我取名为extension&#xff09; // echarts 横向省略文字 鼠标移入显示内容 export const extension…