热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java中关于二叉树的概念以及搜索二叉树详解

二叉树是一种很有用的非线性结构,日常的开发中常会用到,关于二叉树的概念以及搜索二叉树本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,

hello, everyone. Long time no see. 本期文章,我们主要讲解一下二叉树的相关概念,顺便也把搜索二叉树(也叫二叉排序树)讲一下。我们直接进入正题吧!GitHub源码链接

image-20210820143955298

一、二叉树的概念

为什么要使用二叉树?

为什么要用到树呢?因为它通常结合了另外两种数据结构的优点:一种是有序数组,另一种是链表。在树中查找数据项的速度和在有序数组中查找一样快,并且插入数据项和删除数据项的速度也和链表一样。下面,我们先来稍微思考一下这些话题,然后再深入地研究树的细节。

在有序数组中插入数据太慢了,而在链表中查找数据也太慢了。所以到后来就有了二叉树这种数据结构。

树是什么?

在深入讲解二叉树前,我们先简单地认识一下树这个概念。树是由若干个节点组合而成,例如,可以把城市看成节点,将各个城市之间的交通路线看成边。当然说的更准确一点,这个例子更应该是属于图的范畴内,关于图的相关知识点。我们到后面再来讨论。如下图,就是一棵树。

image-20210820150936202

树的相关术语!

​ 如下图所示

image-20210820150349586

根节点

​ 树最顶端的节点称为根节点,一棵树只有一个根节点,一般也是整棵树遍历的开始。

路径

​ 设想一下,从树中的一个节点,沿着边走向另一个节点,所经过的节点顺序排列就称为“路径”。

父节点

​ 就像这个名称一样,在二叉树中扮演“父亲”的角色, 在二叉树中的每一个节点(除了根节点),都有一个边向上可以找到该节点的”父节点“。

子节点

​ 每个节点都可能有一条或多条边向下连接其他节点,下面的这些节点就称为它的“子节点”。

叶节点

​ 没有子节点的节点称为“叶子节点”或简称“叶节点”。树中只有一个根,但是可以有很多叶节点。

子树

​ 每个节点都可以作为“子树”的根,它和它所有的子节点,子节点的子节点等都含在子树中。就像家族中那样,一个节点的子树包含它所有的子孙。

访问

​ 当程序控制流程到达某个节点时,就称为“访问”这个节点,通常是为了在这个节点处执行某种操作,例如查看节点某个数据字段的值或显示节点。如果仅仅是在路径上从某个节点到另一个节点时经过了一个节点,不认为是访问了这个节点。

层(深度)

​ 也就相当于我们人一样,我们这一辈人,就可以看做一层。而爸妈那一辈,又是另外一层。

关键字

​ 如图中所示,每个节点里,有一个数值,这个数值我们就称为关键字。

image-20210820153843923

满二叉树

​ 在一颗二叉树中,如果所有分支节点都存在左子树和右子树,并且所有的叶节点都在同一层上,这样的二叉树,称为满二叉树。如上图所示。

完全二叉树

&#8203; 对一颗具有n个节点的二叉树按从上至下,从左到右的顺序编号,如果编号为i(1 <= i <= n)的节点与同样深度的满二叉树中编号为i的节点在二叉树中的位置完全一样,则这棵树就被称为完全二叉树。

从字面上的意思来看,满二叉树一定是完全二叉树,而完全二叉树不一定是满的。如下图:

image-20210820162709167

二叉树的五大性质

1.在二叉树的第i层上,最多有2(i-1)的次方个节点。例如:第三层上,最多也就有4个节点。

2.深度为k的二叉树,最多有2k的次方 - 1个节点。 例如:深度为3的二叉树,最多也就只有7个节点。

3.对任何一颗二叉树,叶子节点的总数记为n0,度为2的节点的总数记为n2。则n0 = n2 + 1。解释:度为2的节点,指的是该节点左右子节点都有的情况,我们称为度为2的节点。那如果左右子节点,有且仅有一个的时候,我们就叫度为1的节点。

4.具有n个节点的完全二叉树的深度为 log2n + 1。(此处的对数 向下取整)

由满二叉树的定义我们可以知道,深度为k的 满二叉树的节点数n一定等于 2k的次方 - 1。因为这是最多的节点数,再由这个公式,我们就可以倒推出

k = log2(n + 1)。比如节点数为8的满二叉树,深度就是3。

5.如果对一颗有n个节点的完全二叉树的节点,按照从上至下,从左到右,对每一个节点进行编号:则有如下性质:

&#8203; 1). 如果i=1,则该节点就是这棵树的根结点。若i不等于1,则i节点的父节点就是i / 2节点。

&#8203; 2). 如果2i > n,(n为整棵树的总节点数),则i节点没有左子节点,反之就是2i就是左子节点。

&#8203; 3). 如果2i + 1 > n,(n为整棵树的总节点数),则i节点没有右子节点,反之就是2i + 1就是右子节点。

二、搜索二叉树

上面我们讲解完了二叉树的一些基本的概念,现在我们继续来看下一个知识点:搜索二叉树。

定义:一个节点的左子节点的关键字值小于这个节点,右子节点的关键字值大于或等于这个父节点。如下图,就是一个搜索二叉树。

image-20210820164015117

可能会有同学已经发现了一个规律,那就是搜索二叉树的中序遍历的结果就是一个升序的。所以在判断一颗树是不是搜索二叉树时,就可以从这里入手。

知道了定义,我们就可以根据定义来实现相应的代码。

节点结构

class TreeNode {
    int val; //关键字
    TreeNode left; //左子节点
    TreeNode right; //右子节点
    
    public TreeNode(int val) {
        this.val = val;
    }
}

搜索二叉树的整体框架结构

public class BST {
    private TreeNode root; //根结点
    
    public void insert(int val) { //插入新的节点
        
    }
    
    public void remove(int val) { //删除对应的节点
        
    }
    
    public boolean contains(int val) { //查询是否有该值
        
    }
}

我们就一个一个的讲解每一方法具体的实现:

插入

插入新的节点,这个算是比较简单的。我们拿到依次比较当前节点的值和传递进来的形参值,如果形参值更小一点,我们就往左子树上做递归,继续这个操作即可。

//递归解法
public void insert(int val) {
    root = process(val, root);
}

private TreeNode process(int val, TreeNode node) {
    if (node == null) { //如果当前节点为null,说明已经走到头了,此时创建节点,返回即可
        return new TreeNode(val);
    } 
    if (val 
//非递归解法
public void insert(int val) {
    TreeNode node = new TreeNode(val); //先创建好节点
    TreeNode parent = null; //父节点,用于连接新的节点
    TreeNode cur = root; //当前移动的节点
    
    if (root == null) {
        root = node; //还没有根结点的情况
    } else {
        while (true) {
            parent = cur;
            if (val 

递归与非递归的解法,差异只是在于空间复杂度。当整棵树很大时,递归去调用,就会耗费大量的栈空间。而非递归的解法,只是耗费了几个引用的空间。

请添加图片描述

删除

删除是一个比较难的点,删除之后,还需要保持搜索二叉树的结构。所以我们需要分为三种情况:

  • 被删除节点是叶节点。
  • 被删除节点只有一个孩子节点。
  • 被删除节点有两个孩子节点。

我们需要循环遍历这颗树,找到需要被删除的节点,并且在遍历的过程中,还需要记录被删除节点的父节点是谁,以及被删除节点是父节点的左孩子还是右孩子。所以循环时,有三个变量,分别是parent、cur和isLeftChild。

在找到需要被删除的节点后。再对这个节点进行判断,看这个节点是叶节点?还是只有一个孩子节点?又或者是有两个孩子节点的情况。

  1. 如果是叶节点,parent的left(或者是right)置为null
  2. 如果只有一个节点,我们就需要绕过cur节点,直接连接cur的left或者right
  3. 如果是有两个节点,我们就需要找到cur的后继节点。也就是cur的右子树中,最小的节点。

其次我们还需要判断被删除的节点,是不是root根结点?如果是,就需要更换根结点。

image-20210820214905537

非递归版本大致框架:

image-20210820213323974

//非递归版本
public boolean remove(int val) { //删除对应的节点
    if (root == null) {
        throw new RuntimeException("root is null.");
    }

    TreeNode parent = root;
    TreeNode cur = root;
    boolean isLeftChild = true;

    while (cur != null && cur.val != val) { //循环查找需要被删除的节点
        parent = cur;
        if (val 
//递归版本
public void remove2(int val) {
    if (root == null) {
        throw new RuntimeException("root is null.");
    }

    process2(val, root);
}

private TreeNode process2(int val, TreeNode node) {
    if (node == null) {
        return null;
    }
    if (val  node.val){ //大于
        node.right = process2(val, node.right);
    } else if (node.left != null && node.right != null) { //上面的if没成立,说明val相等。这里是两个孩子节点的情况
        
        node.val = getMinNodeVal(node.right); //覆盖右子树中最小的节点值
        
        node.right = process2(node.val, node.right); // 重新对已经覆盖的数值进行删除
        
    } else { //只有一个孩子节点或者没有节点的情况
        node = node.left != null&#63; node.left : node.right;
    }
    return node;
}

private int getMinNodeVal(TreeNode node) {
    TreeNode pre = null;
    TreeNode cur = node;
    while (cur != null) {
        pre = cur;
        cur = cur.left;
    }
    return pre.val;
}

递归版本的删除,只是将右子树最小节点的值,赋值给了cur,然后递归调用去删除右子树上最小值的节点。

最后一个contains方法就简单了,遍历整颗二叉树,找到了val就返回true,否则返回false。

public boolean contains(int val) {
    TreeNode cur = root;
    while (cur != null) {
        if (cur.val == val) {
            return true;
        } else if (val 

最后自己再写一个中序遍历的方法,看看自己写的代码是否正确了呢。切记:搜索二叉树中序遍历的结果,一定是一个升序的。不知道怎么写遍历方法的,可以看一下前期文章:通俗易懂讲解C语言与Java中二叉树的三种非递归遍历方式。
好啦,本期文章就到此结束啦,我们下期见!!!

到此这篇关于Java中关于二叉树的概念以及搜索二叉树详解的文章就介绍到这了,更多相关Java 二叉树内容请搜索编程笔记以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程笔记!


推荐阅读
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • 开源Keras Faster RCNN模型介绍及代码结构解析
    本文介绍了开源Keras Faster RCNN模型的环境需求和代码结构,包括FasterRCNN源码解析、RPN与classifier定义、data_generators.py文件的功能以及损失计算。同时提供了该模型的开源地址和安装所需的库。 ... [详细]
  • Java如何导入和导出Excel文件的方法和步骤详解
    本文详细介绍了在SpringBoot中使用Java导入和导出Excel文件的方法和步骤,包括添加操作Excel的依赖、自定义注解等。文章还提供了示例代码,并将代码上传至GitHub供访问。 ... [详细]
  • 本文介绍了Java调用Windows下某些程序的方法,包括调用可执行程序和批处理命令。针对Java不支持直接调用批处理文件的问题,提供了一种将批处理文件转换为可执行文件的解决方案。介绍了使用Quick Batch File Compiler将批处理脚本编译为EXE文件,并通过Java调用可执行文件的方法。详细介绍了编译和反编译的步骤,以及调用方法的示例代码。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • 如何使用Python从工程图图像中提取底部的方法?
    本文介绍了使用Python从工程图图像中提取底部的方法。首先将输入图片转换为灰度图像,并进行高斯模糊和阈值处理。然后通过填充潜在的轮廓以及使用轮廓逼近和矩形核进行过滤,去除非矩形轮廓。最后通过查找轮廓并使用轮廓近似、宽高比和轮廓区域进行过滤,隔离所需的底部轮廓,并使用Numpy切片提取底部模板部分。 ... [详细]
  • 在IDEA中运行CAS服务器的配置方法
    本文介绍了在IDEA中运行CAS服务器的配置方法,包括下载CAS模板Overlay Template、解压并添加项目、配置tomcat、运行CAS服务器等步骤。通过本文的指导,读者可以轻松在IDEA中进行CAS服务器的运行和配置。 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
  • 判断编码是否可立即解码的程序及电话号码一致性判断程序
    本文介绍了两个编程题目,一个是判断编码是否可立即解码的程序,另一个是判断电话号码一致性的程序。对于第一个题目,给出一组二进制编码,判断是否存在一个编码是另一个编码的前缀,如果不存在则称为可立即解码的编码。对于第二个题目,给出一些电话号码,判断是否存在一个号码是另一个号码的前缀,如果不存在则说明这些号码是一致的。两个题目的解法类似,都使用了树的数据结构来实现。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了源码分析--ConcurrentHashMap与HashTable(JDK1.8)相关的知识,希望对你有一定的参考价值。  Concu ... [详细]
author-avatar
Effy
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有