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

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

目录


前言

你好,认识一下,我是树

二叉树与二叉排序树

二叉排序树特点

为什么说二叉排序树查询效率要高于链表呢?

元素的类型

比较器

手写二叉排序树

定义一棵二叉树

增加元素

查询元素

修改元素

删除元素

遍历二叉树

重写toString

二叉排序树的极端情况

平衡二叉树

红黑树

保证平衡

红黑树的规则

红黑树的应用

散列表

向散列表中存数据

hashCode()方法特点

链表的产生原因

链表存在的问题

数组的扩容

结语


前言


学习是一个渐进的过程,如果还没看过我前面写的有关数据结构的文章可以先去看完再回来看这篇,在写这篇之前,我已经预见到了这篇博客会比较长,而且概念性的东西会比较多,如果你对树真的有兴趣,想要一看究竟,那么欢迎你继续读下去,如果你只是临时起意,劝你三思。树在各个语言领域都可谓谈之色变,九成九的程序员在开发中根本就没用过树,但实际上又在不知不觉间用了。那么树是什么?树在Java开发中又有哪些代言人,让我们通过这篇博客来认识下吧。


你好,认识一下,我是树


树,我们在数据库索引的数据结构中已经给大家介绍过,为了方便大家理解,这里给没看过的小伙伴重新写一下:


Tree就是树,顾名思义,像树一样的结构,只要是树,就一定拥有四个属性:


根结点:位于顶端且仅有一个

高度:整个树的层次

度:树中最大子节点数

叶子结点:位于最底层且没有子节点,或者说度为0的节点

看到这里,想必你已经知道了什么是树,下面,我给出一张图让大家看看树的基本数据结构:

1.png

看上去很赏心悦目,这是一棵平衡二叉树,也是二叉树的一种,仅做展示,没其他意义,请不要额外联想。在写之前,我们要先明白一件事,集合中是不能添加重复元素的,先不要问,后面你就知道为什么了。


二叉树与二叉排序树


顾名思义,度为2的树就是二叉树,上面的图就是一个二叉树,那什么是二叉排序树呢?二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。是数据结构中的一类。在一般情况下,查询效率比链表结构要高。如我先前所言,这里我们又提到了链表,如果看过前面文章的小伙伴可以加深对链表的理解。


二叉排序树特点


元素不能重复

左子树中节点均小于根结点

右子树中节点均大于根节点

这一点从此图也能看的出来:

1.png


为什么说二叉排序树查询效率要高于链表呢?


如果是一个长度为n的链表,最差的结果就是查n次(单向链表,双向链表查询方式可查看前面关于链表的博客),在二叉排序树中,看图,每次比较后都会排除总量一半的数据,所以查询的效率非常高。这个二叉排序树中有7个元素,假设n为7,链表最多要查7次,这里最多也就是3次,如果数据量翻倍,差别会更大。


元素的类型


二叉排序树中的元素的类型可以是其他的引用类型,但是他们之间要能比较大小,这就需要他们都实现comparable接口,在类中定义比较的规则,这样,对象也是可以比较大小的。


比较器


比较器分为两种:


comparable:内比较器,实现该接口后,需要重写内部抽象方法,在类内部定义比较规则,Collections.sort(list)就是如此

comparator:外比较器,比较规则定义在类的外部,通常用于在不改变类原有比较规则的情况下,使用新的/临时的比较规则,此时可以在类的外部实现该接口,定义新的比较规则。Collections.sort(list,comparator)

对集合排序有一个机制,这是前提:  sort方法内部会自动调用集合中元素的compareTo方法


没有这个前提,我们接下来讲的就没有意义!


说起来很笼统,所以我们通过代码来说明:


我们先定义一个学生类,并重写抽象方法:

public class Student implements Comparable<Student>{
    private Integer id;
    private String name;
    private Integer age;
    @Override
    public int compareTo(Student o) {
        return this.id-o.getId();
    }
}

接着我们来把学生按照id升序排列:

List<Student> list = new ArrayList<>();
for (int i=2;i<=10;i++){
    Student student = new Student(i,"student"+i,20+i);
    list.add(student);
}
Student student1 = new Student(1,"Ammy",20);
list.add(student1);
list.forEach(student -> System.out.println(student));
//对list按id升序排列
Collections.sort(list);
list.forEach(student -> System.out.println(student));

可以看到,在学生中我们已经重写了compareTo方法,这里我们就可以直接通过sort方法来处理。


上面我们采用的是内比较器,接下来我们改变规则,采用外比较器来处理:

//按age升序排列
Collections.sort(list, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
});
list.forEach(student -> System.out.println(student));

如果要比较name,因为name肯定是String类型,使用String默认的比较器就可以,和age是不一样的,还是写一下吧:

//按照name升序排列
Collections.sort(list, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o2.getName().compareTo(o1.getName());
    }
});
list.forEach(student -> System.out.println(student));


手写二叉排序树


接下来是我们的重点,手写二叉排序树,带大家理解排序的逻辑和原理。


定义一棵二叉树


其本质上是数据结构,是用来存储数据的,所以我们一般用泛型,让外部告知具体类型,而不能把类型写死。

public class BinarySearchTree<E extends Comparable<E>> {
}

而为了让传入的类型支持比较,必须要对泛型继承Comparable,这也是一个难点,很多人在这里容易写错。


定义完了类,那接着就需要定义根节点:

public class BinarySearchTree<E extends Comparable<E>> {
    //根节点
    private Node root;
    //定义内部类表示节点
    private class Node{
        private E ele;  //节点中保存的元素对象
        private Node left;  //左子树指向的节点
        private Node right;  //右子树指向的节点
        //定义有参构造方法,用于创建节点
        Node(E ele){
            this.ele = ele;
        }
    }
}

这时,一棵树的基本元素就定义好了,接下来就是我们所熟悉的增删改查方法,但写起来,可能并没那么简单。

目录
相关文章
|
1月前
|
Java API Maven
如何使用Java开发抖音API接口?
在数字化时代,社交媒体平台如抖音成为生活的重要部分。本文详细介绍了如何用Java开发抖音API接口,从创建开发者账号、申请API权限、准备开发环境,到编写代码、测试运行及注意事项,全面覆盖了整个开发流程。
108 10
|
29天前
|
监控 Java API
如何使用Java语言快速开发一套智慧工地系统
使用Java开发智慧工地系统,采用Spring Cloud微服务架构和前后端分离设计,结合MySQL、MongoDB数据库及RESTful API,集成人脸识别、视频监控、设备与环境监测等功能模块,运用Spark/Flink处理大数据,ECharts/AntV G2实现数据可视化,确保系统安全与性能,采用敏捷开发模式,提供详尽文档与用户培训,支持云部署与容器化管理,快速构建高效、灵活的智慧工地解决方案。
|
19天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
38 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
6天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
52 13
|
11天前
|
算法 Java API
如何使用Java开发获得淘宝商品描述API接口?
本文详细介绍如何使用Java开发调用淘宝商品描述API接口,涵盖从注册淘宝开放平台账号、阅读平台规则、创建应用并申请接口权限,到安装开发工具、配置开发环境、获取访问令牌,以及具体的Java代码实现和注意事项。通过遵循这些步骤,开发者可以高效地获取商品详情、描述及图片等信息,为项目和业务增添价值。
44 10
|
5天前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
40 2
|
14天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
1月前
|
开发框架 Java 关系型数据库
Java哪个框架适合开发API接口?
在快速发展的软件开发领域,API接口连接了不同的系统和服务。Java作为成熟的编程语言,其生态系统中出现了许多API开发框架。Magic-API因其独特优势和强大功能,成为Java开发者优选的API开发框架。本文将从核心优势、实际应用价值及未来展望等方面,深入探讨Magic-API为何值得选择。
42 2
|
1月前
|
监控 前端开发 Java
【技术开发】接口管理平台要用什么技术栈?推荐:Java+Vue3+Docker+MySQL
该文档介绍了基于Java后端和Vue3前端构建的管理系统的技术栈及功能模块,涵盖管理后台的访问、登录、首页概览、API接口管理、接口权限设置、接口监控、计费管理、账号管理、应用管理、数据库配置、站点配置及管理员个人设置等内容,并提供了访问地址及操作指南。
|
1月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
64 1