Java开发 - 树(二叉树,二叉排序树,红黑树)(二)

简介: Java开发 - 树(二叉树,二叉排序树,红黑树)

增加元素


判断root是否为null
  -为null,则将元素封装成节点,称为root,返回true
  -不为null,和root进行比较,比较的目的是将元素尝试添加为root的left/right子树
    - 和root相等,添加失败,返回false,结束
    - 大于>root,尝试添加为root的right;判断right是否为null,
      - 为null,则让新元素封装成节点,称为right,添加成功,返回true
      - 不为null,继续和right的节点进行比较,尝试将元素添加为right的left/right子树
    - 小于,同理

增加元素需要先判断root是否为null


为null,将元素封装成根结点,返回true;

非null,和root进行比较,这是为了判断新元素是添加左子树还是右子树,但不一定添加成功

和root相等,那么添加失败,返回false,结束

大于root,尝试添加为root的右子树,判断right是否为null;

为null,将新元素封装成节点,成为右子树right,添加完成,返回true;

不为null,和right节点进行比较,尝试将该元素添加为right的left/right子树(这里递归就出现了)

小于root,尝试添加为root的左子树,判断left是否为null;

为null,将新元素封装成节点,成为左子树left,添加完成,返回true;

不为null,和left节点进行比较,尝试将该元素添加为left的left/right子树(这里递归就出现了)


思路理清了,那我们就开始写代码:

//添加元素
    public boolean add(E e){
        //判断root是否为null,为null,成为根节点
        if (root==null) {
            root = new Node(e);
            return true;
        }
        //root不为null,添加
        return root.append(e);
    }

Node内的添加方法:

       //向某个节点上添加给定的元素,尝试让新元素称为其左/右子树
        public boolean append(E e) {
            if (e.compareTo(ele) == 0) {
                return false;
            } else if (e.compareTo(ele) < 0) {
                if (left == null) {
                    left = new Node(e);
                    return true;
                }
                return left.append(e);
            } else {
                if (right == null) {
                    right = new Node(e);
                    return true;
                }
                return right.append(e);
            }
        }


查询元素


查询元素也需要先判断root是否为null


为null,树中没有节点,返回null;

非null,判断root节点是否为需要查找的元素

相等,root节点就是需要的元素,返回root节点,结束;

大于ele,就到节点的right去查找相等节点,判断right节点是否存在

为null,说明查询的元素不在此树中,返回null

非null,判断right节点是不是要找的节点,往后一直重复上述操作,直到找到或找不到为止

小于ele,就到节点的left去查找相等节点,判断left节点是否存在

为null,说明查询的元素不在此树中,返回null

非null,判断left节点是不是要找的节点,往后一直重复上述操作,直到找到或找不到为止


用代码来写下这个过程:

    //根据元素查询对应的节点对象
    public Node get(E e){
        //判断root是否为null
        if (root==null)
            return null;
        //root不为空,则判断root是否为目标节点
        return root.isDestNode(e);
    }

Node内的查询:

        //用于判断调用方法的节点是否为目标节点
        public Node isDestNode(E e) {
            //判断e是否和当前节点的元素相等,若相等,说明为目标节点,返回当前节点即可
            if (e.compareTo(ele)==0)
                return this;
            else if (e.compareTo(ele)>0){  //e大于当前节点,到right上继续查找
                //若right为null,说明目标元素不存在树中,返回null
                if (right==null)
                    return null;
                return right.isDestNode(e);
            }else {  //e小于当前节点,到left上继续查找
                if (left==null)
                    return null;
                return left.isDestNode(e);
            }
        }


修改元素


修改其实很简单,首先是查找,查找到之后,直接将新的元素指向那个节点。


我们可以定义这个方法如下:

set(E ele, E newEle)

修改理论上是可行的,但是在修改的时候,会不会引起重新排序呢?这对整个树结构是有影响的,代码似乎也没有刚开始说的那么简单了,重新排序对性能也有影响,所以二叉树可不提供修改方法。


删除元素


这要看删的是谁,如果删除的是叶子结点,那直接删了就好,不需要做什么判断,也不存在排序问题。一旦删除的是中间的节点,看下图:

1.png

假如删的是2或者6,这时候就要考虑谁上去的问题了。


所以删除分为三种情况:


删除叶子结点:将其父节点指向它的引用置为null;

删除有一棵子树的节点:将其父节点的指向变更为被删除的节点的子节点;

删除有两颗子树的节点:让被删除节点的前驱节点或者后继节点上来替换此节点;

前驱节点和后继节点是指升序排列后,删除节点的前一个元素叫做前驱节点,删除节点的后一个元素叫做后继节点

代码写起来会非常复杂,体验很差,所以就不写了,理解了就行,有兴趣的同学可自行尝试。


遍历二叉树


遍历的方式分为三种:

先序遍历:根  左  右

中序遍历:左  根  右

后序遍历:左  右  根


什么意思呢?我们根据一棵树来说:

1.png

先序遍历:4,2,1,3,6,5,7(即先根,后左,再右)

中序遍历:1,2,3,4,5,6,7(即先左,后根,再右,也叫升序排列)

后序遍历:1,3,2,5,7,6 ,4(即先左,后右,再根)


重写toString


目的是为了实现输出引用,将二叉树中的元素遍历,并以此格式输出:[1,2,3,4,5,6,7]输出,没有节点,返回[]。

    //重写toString
    @Override
    public String toString() {
        //判断root是否为null,为null,返回"[]"
        if (root==null)
            return "[]";
        //进行中序遍历
        StringBuilder builder = new StringBuilder("[");
        return root.middleOrder(builder).replace(builder.length()-1,builder.length(),"]").toString();

Node内的方法:

        public StringBuilder middleOrder(StringBuilder builder) {
            //左
            if (left!=null){
                left.middleOrder(builder);
            }
            //取根的值
            builder.append(ele).append(",");
            //取右
            if (right!=null){
                right.middleOrder(builder);
            }
            //以上三步操作结束,得到遍历树后的元素拼接结果,将最后的逗号替换为]
            return builder;
        }

中序遍历这里有个递归,我们要明白的是,最终所有的代码都要取到根的值,这个节点是相对的,每个节点都会是这个根,这样才能拼接出字符串。大家可以动手尝试下其他遍历方式的写法,这里不再赘述。


二叉排序树的极端情况

1.png

这就是失衡二叉树,失衡二叉树是不希望存在的,因为它失去了二叉树本身的优势特点,这种状态和单向链表一样,单向链表的效率如何,想必大家都是知道的。


所以在设计数据结构的时候,已经考虑了这种情况,出现了两种新的数据结构,他们都基于二叉排序树,保留了其优势,又避免了这个缺点。


他们是平衡二叉树和红黑树。

目录
相关文章
|
4月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
5月前
|
消息中间件 人工智能 Java
抖音微信爆款小游戏大全:免费休闲/竞技/益智/PHP+Java全筏开源开发
本文基于2025年最新行业数据,深入解析抖音/微信爆款小游戏的开发逻辑,重点讲解PHP+Java双引擎架构实战,涵盖技术选型、架构设计、性能优化与开源生态,提供完整开源工具链,助力开发者从理论到落地打造高留存、高并发的小游戏产品。
|
5月前
|
存储 Java 关系型数据库
Java 项目实战基于面向对象思想的汽车租赁系统开发实例 汽车租赁系统 Java 面向对象项目实战
本文介绍基于Java面向对象编程的汽车租赁系统技术方案与应用实例,涵盖系统功能需求分析、类设计、数据库设计及具体代码实现,帮助开发者掌握Java在实际项目中的应用。
235 0
|
6月前
|
JavaScript 安全 前端开发
Java开发:最新技术驱动的病人挂号系统实操指南与全流程操作技巧汇总
本文介绍基于Spring Boot 3.x、Vue 3等最新技术构建现代化病人挂号系统,涵盖技术选型、核心功能实现与部署方案,助力开发者快速搭建高效、安全的医疗挂号平台。
340 3
|
6月前
|
安全 Java 数据库
Java 项目实战病人挂号系统网站设计开发步骤及核心功能实现指南
本文介绍了基于Java的病人挂号系统网站的技术方案与应用实例,涵盖SSM与Spring Boot框架选型、数据库设计、功能模块划分及安全机制实现。系统支持患者在线注册、登录、挂号与预约,管理员可进行医院信息与排班管理。通过实际案例展示系统开发流程与核心代码实现,为Java Web医疗项目开发提供参考。
347 2
|
6月前
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
506 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
|
7月前
|
并行计算 Java API
Java List 集合结合 Java 17 新特性与现代开发实践的深度解析及实战指南 Java List 集合
本文深入解析Java 17中List集合的现代用法,结合函数式编程、Stream API、密封类、模式匹配等新特性,通过实操案例讲解数据处理、并行计算、响应式编程等场景下的高级应用,帮助开发者提升集合操作效率与代码质量。
342 1
|
7月前
|
安全 Java API
Java 17 及以上版本核心特性在现代开发实践中的深度应用与高效实践方法 Java 开发实践
本项目以“学生成绩管理系统”为例,深入实践Java 17+核心特性与现代开发技术。采用Spring Boot 3.1、WebFlux、R2DBC等构建响应式应用,结合Record类、模式匹配、Stream优化等新特性提升代码质量。涵盖容器化部署(Docker)、自动化测试、性能优化及安全加固,全面展示Java最新技术在实际项目中的应用,助力开发者掌握现代化Java开发方法。
335 1
|
6月前
|
移动开发 Cloud Native 安全
Java:跨平台之魂,企业级开发的磐石
Java:跨平台之魂,企业级开发的磐石