【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(一)

news2025/1/15 13:18:18

前言

终于到了之前C语言没有讲过的数据结构了,那就是二叉树了,关于二叉树的学习难度确实比前面学习的数据结构都要难一点,所以我们这个关于二叉树的博客大概率是有好几篇的。如有哪里出现错误也欢迎指出唔。

二叉树的概念

Java 中的二叉树是一种基础的数据结构,它是由节点组成的树形结构,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。二叉树的节点通常包含三个部分:节点的数据域、指向左子节点的指针和指向右子节点的指针。

我们通常把没有父节点(双亲节点)的节点叫做根节点,平常说的”这个根“,”它的根“中的根就是值根节点。

比如上面的”1“为整个树的根节点,也可以说”4“是 5和6的根因为其实4,5,6也可以说其组成了一棵树,组成的这棵树我们叫做子树。

二叉树的性质:
  1. 定义性:二叉树是节点的集合,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。
  2. 层级性:二叉树的节点按照层次排列,根节点位于第一层,其子节点位于第二层,以此类推。
  3. 有序性:在二叉搜索树(Binary Search Tree, BST)中,每个节点的值都大于(或等于)其左子树中所有节点的值,并且小于(或等于)其右子树中所有节点的值。
  4. 递归性:二叉树可以递归地定义为:一个节点(包含数据域和两个指向子树的指针域)或者为空。
  5. 树的深度:二叉树的深度是根节点到最远叶子节点的最长路径上的边数。
  6. 节点的度:一个节点的度是指该节点拥有的子节点数量。在二叉树中,节点的度最大为2。
  7. 树的广度:二叉树的广度是指树的层数。
  8. 叶子节点:二叉树中没有子节点的节点称为叶子节点或外部节点。
  9. 内部节点:至少有一个子节点的节点称为内部节点。
  10. 树的高度:二叉树的高度是从根节点到任意叶子节点的最长路径上的边数。
  11. 子树:任何一个节点及其所有后代构成的二叉树称为该节点的子树。
  12. 兄弟节点:具有相同父节点的节点称为兄弟节点。
  13. 祖先节点:从根到该节点所经过的节点序列称为该节点的祖先。
  14. 后代节点:以某节点为根的子树中的任一节点都称为该节点的后代。
  15. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 (i>0)个结点
  16. 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 (k>=0)
  17. 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1

这些性质是理解和操作二叉树的基础,它们在算法设计和数据结构分析中扮演着重要角色。

比较需要注意的是第17点性质。

三种特殊的二叉树
  1. 完全二叉树:如果一个二叉树的所有层都被完全填满,除了最后一层,并且最后一层的节点尽可能地集中在左侧,这样的二叉树称为完全二叉树。

也就是说在如果最后一层在左边没有填满,右边就突然有一个节点,那么他就不是完全二叉树。如下就不是一棵完全二叉树:

  1. 满二叉树:如果一个二叉树的所有节点都恰好有两个子节点,这样的二叉树称为满二叉树。

  2. 平衡二叉树:如果二叉树的任何两个叶子节点的深度之差不超过1,这样的二叉树称为平衡二叉树。AVL树和红黑树是两种常见的自平衡二叉搜索树。

这里叶子节点有”9“,”15“,”7“,他们之间的深度之差都不超过1,所以它是一棵平衡二叉树。


二叉树的数据储存---链式储存

孩子表示法

class Node<E>{
    E val;    // 数据域
    Node left;  //左孩子的引用,常常代表左孩子为根的整棵左子树
    Node right; //右孩子的引用,常常代表左孩子为根的整棵左子树
    

}

孩子双亲表示法

class Node<E>{
    E val;    // 数据域
    Node left;  //左孩子的引用,常常代表左孩子为根的整棵左子树
    Node right; //右孩子的引用,常常代表左孩子为根的整棵左子树
    Node parent; //当前节点的根节点
    }
    

注:Java 标准库中包含了一些与二叉树相关的类,但它们并不是直接的二叉树实现,而是以不同的数据结构形式存在。

二叉树主要的三种遍历

前提准备:因为我们现在还没学习真正的创建一颗二叉树,所以我们先用一种笨的方法创建一棵二叉树,代码如下:

class BinaryTree{
    public static class BTNode{
        BTNode left;
        BTNode right;
        int value;
        BTNode(int value){
            this.value = value;
        }
    }
    private BTNode root;
    public void createBinaryTree(){
        BTNode node1 = new BTNode(1);
        BTNode node2 = new BTNode(2);
        BTNode node3 = new BTNode(3);
        BTNode node4 = new BTNode(4);
        BTNode node5 = new BTNode(5);
        BTNode node6 = new BTNode(6);
        root = node1;
        node1.left = node2;
        node2.left = node3;
        node1.right = node4;
        node4.left = node5;
        node4.right = node6;
    }
 }   

这样我们写了一个createBinaryTree()方法,用来创建我们的一颗二叉树,这棵二叉树是这样的:

那么接下来我们就来学习三种二叉树主流遍历方法

前序遍历

学习二叉树遍历通常都是学习其中一种就可以举一反三,很容易就会其它两种遍历了。我们现在学习的这种遍历方法有递归方法和非递归方法。前序遍历就是先遍历根然后打印,在遍历左节点,在遍历右节点。口诀:”左右根“

前序遍历结果:1 2 3 4 5 6

递归前序遍历代码如下:
void preOrder(BTNode root){
    if(root==null){
        return;
    }

    System.out.print(root.value+" ");
    preOrder(root.left);
    preOrder(root.right);



}

代码很简单,但是对于没学到的小伙伴可能就懵逼了。那我们就用图来详细解析一下这个递归的遍历方法

这一张图是总的二叉树前序遍历图,画出了递归和回退过程,对于有一点递归基础的应该是可以看懂的。

然后上面这两张图就是根节点的整个左子树的递归过程,也就是节点”1“,”2“,”3“节点的递归回退过程,后面就是到右子树的递归过程,右子树的递归过程我就不画了,思维和左子树是一样的。

非递归前序遍历方法
public void preOrderNor(TreeNode root) {

        if(root == null) return;

        Stack<TreeNode> stack = new Stack<>();

        TreeNode cur = root;



        while (cur != null || !stack.isEmpty()) {

            while (cur != null) {

                stack.push(cur);

                System.out.print(cur.val + " ");

                cur = cur.left;

            }

            TreeNode top = stack.pop();

            cur = top.right;

        }

    }

解析:

非递归的遍历,首先我们遍历是需要返回前一个节点的,所以我们必须用到一个栈来进行储存我们的节点,然后我们首先创建一个循环只要cur不等于null和栈不为空就进入循环,cur不为空是因为首次进入的时候栈是空的,为了保证刚开始能进入循环就加了这么个判断条件。然后在镶嵌一个循环,这个循环就是遍历左树的每遍历一个就进栈一个为了储存节点好让后面返回节点遍历右树,然后因为这是前序遍历,所以我们要先打印在 cur = cur.left; 然后除了第一个循环就可以开始遍历右树了,这时我们就需要先出栈来返回上一个节点。


中序遍历

中序遍历和前序遍历递归思维都是一样的,就不再画图给大家解析了。中序遍历就是先遍历完左节点在回退到根进行打印,在遍历右节点,回退过程中在打印口诀:”左右根“

中序遍历结果:3 2 1 5 4 6

递归前中遍历代码如下:
void inOrder(BTNode root){
    if(root==null){
        return;
    }


    inOrder(root.left);

    System.out.print(root.value+" ");


    inOrder(root.right);



}

非递归中序遍历方法
public void inOrderNor(TreeNode root) {

        if(root == null) return;

        Stack<TreeNode> stack = new Stack<>();

        TreeNode cur = root;



        while (cur != null || !stack.isEmpty()) {

            while (cur != null) {

                stack.push(cur);

                cur = cur.left;

            }

            TreeNode top = stack.pop();

            System.out.print(top.val + " ");



            cur = top.right;

        }

    }

这个中序遍历的非递归方法还是和前序遍历思路一样,也就是打印的语句不一样罢了,就不再次解析了,理解了前序,中序的这个方法肯定是会理解的。重点放在后序遍历的非递归方法。


 


后序遍历

后续遍历就是遍历完当前节点的左右子树后,才会打印这个节点口诀:”左右根“

中序遍历和前序遍历递归思维都是一样的,就不再画图给大家解析了。

后序遍历结果:3 1 5 6 4 1

递归后序遍历代码如下:
void inOrder(BTNode root){
    if(root==null){
        return;
    }


    inOrder(root.left);

    System.out.print(root.value+" ");


    inOrder(root.right);



}

非递归后序遍历方法

public void postOrderNor(TreeNode root) {

        if(root == null) return;

        Stack<TreeNode> stack = new Stack<>();

        TreeNode cur = root;

        TreeNode prev = null;

        while (cur != null || !stack.isEmpty()) {

            while (cur != null) {

                stack.push(cur);

                cur = cur.left;

            }

            TreeNode top = stack.peek();

            if (top.right == null || top.right == prev) {

                System.out.print(top.val + " ");

                stack.pop();

                prev = top;

            } else {

                cur = top.right;

            }

        }

    }

为什么说后续遍历的非递归方法需要注意呢?

因为只要稍微不注意代码就会进入死循环。那么容易死循环的地方在哪里呢?

很多人可能会仿照前序和中序的代码在进入右树后面的语句加个判断左右是否为空,然后进行打印,但是却不会想到打印完后,回退到父节点时,此时的父节点已经在之前遍历过了。比如如果二叉树加了一个节点:

这个图如果失去了top.right == prev;这个语句就会死循环,它会在3和13不断死循环,所以我们就得在遍历右树时加上top.right == prev;这样就不会回退到3时,在进入13了。


这篇文章的二叉树就到这结束了,这一篇注要讲如何遍历二叉树。【用Java学习数据结构系列】震惊,二叉树原来是要这么学习的(一)

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

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

相关文章

【Oracle点滴积累】解决ORA-20000: ORA-12899: value too large for column错误的方法

广告位招租&#xff01; 知识无价&#xff0c;人有情&#xff0c;无偿分享知识&#xff0c;希望本条信息对你有用&#xff01; 今天和大家分享ORA-20000: ORA-12899: value too large for column错误的解决方法&#xff0c;本文仅供参考&#xff0c;谢谢&#xff01; A fatal…

【C语言】二叉树的深度理解

&#x1f36c;个人主页&#xff1a;Yanni.— &#x1f308;数据结构&#xff1a;Data Structure.​​​​​​ &#x1f382;C语言笔记&#xff1a;C Language Notes 前言 在之前学习了二叉树的基本概念&#xff0c;但二叉树有着更深入知识理解&#xff0c;这篇文章可以帮助大…

2 种方式申请免费 SSL 证书,阿里云 Certbot

如何使用免费的 SSL 证书&#xff0c;有时在项目中需要使用免费的 SSL 证书&#xff0c;Aliyun 提供免费证书&#xff0c;三个月有效期&#xff0c;可以直接在aliyun 申请&#xff0c;搜索 SSL 证书&#xff0c;选择测试证书。 Aliyun 证书需要每三月来来换一次&#xff0c;页…

ubuntu中python 改为默认使用python3,pip改为默认使用pip3

一、安装pip和python&#xff08;有的话可跳过&#xff09; 更新软件源 sudo apt update !!!apt和apt-get apt apt-get、apt-cache 和 apt-config 中最常用命令选项的集合。 部分截图为apt-get&#xff0c;建议直接用apt 安装pip和python ubuntu 18.04和更高版本默认安…

申请中的专利可以用来申报高企吗

申请中的专利可以用来申报高企吗&#xff1f; 申请中的专利是否可以用于高新技术企业&#xff08;简称“高企”&#xff09;申报时&#xff0c;我们需要深入了解高企认定的具体条件和要求&#xff0c;以及专利在其中的角色和地位。 高新技术企业认定的基本条件 高新技术企业认定…

图像分类数据集|新冠肺炎|3类

新冠肺炎图像分类数据集&#xff0c;总共三类&#xff0c;获取地址在最后&#xff1a; 训练集&#xff1a; 251 测试集&#xff1a; 66 类别名&#xff1a; [‘Covid’, ‘Normal’, ‘Viral Pneumonia’] 数据集整理不易&#xff0c;获取地址如下&#xff1a; https://ite…

VM虚拟机-Ubuntu莫名其妙断网及解决

问题解决 由于Ubuntu下访问GitHub总是很慢&#xff0c;所以在鼓捣解决方法时不知怎么的直接给干的没法访问互联网了&#xff0c;虽然之前保存了快照&#xff0c;但恢复了几个快照都是没网&#xff08;ping不通&#xff09;&#xff0c;后续的解决方法如下。 在命令行中输入 …

EchoMimic—语音驱动图像

简介 EchoMimic是阿里巴巴达摩院推出的一个AI驱动的口型同步技术项目。通过一段音频和一张人物的面部照片&#xff0c;生成一个看起来像是在说话的视频&#xff0c;其中的人物口型动作与音频中的语音完美匹配。   EchoMimic优点&#xff1a; 1.口型同步生成&#xff1a;能根据…

WebRTC音视频开发读书笔记(六)

数据通道不仅可以发送文本消息, 还可以发送图片、二进制文件,将其类型binaryType属性设置成arraybuffer类型即可. 九\、文件传输 1、文件传输流程 &#xff08;1&#xff09;使用表单file打开本地文件 &#xff08;2&#xff09;使用FileReader读取文件的二进制数据 &#…

【PyCharm】配置“清华镜像”地址

文章目录 前言一、清华镜像是什么&#xff1f;二、pip是什么&#xff1f;三、具体步骤1.复制镜像地址2.打开PyCharm&#xff0c;然后点击下图红框的选项3.在弹出的新窗口点击下图红框的选项进行添加4.在URL输入框中粘贴第一步复制的地址&#xff0c;名字可以不更改&#xff0c;…

电力调度控制台作为智能电网的中枢大脑,引领能源高效调度新时代

在当今这个能源需求日益增长、电力网络日益复杂的时代&#xff0c;电力调度控制台作为智能电网的核心组成部分&#xff0c;正扮演着至关重要的角色。它不仅是电力系统中信息汇聚与决策输出的中枢大脑&#xff0c;更是实现电力资源优化配置、保障电网安全稳定运行的关键所在。 智…

MYSQL查询规范:索引

前言 工作有段时间了&#xff0c;现在看以前写的代码、sql之类的&#xff0c;实属辣眼睛。 这里将给出一些目前遇到的MYSQL查询规范&#xff08;索引&#xff09;&#xff0c;并长期更新 索引 众所周知&#xff0c;索引能提高数据查询效率&#xff08;前提是该字段被用在WHERE、…

Springboot邮件发送:如何配置SMTP服务器?

Springboot邮件发送集成方法&#xff1f;如何提升邮件发送性能&#xff1f; 对于使用Springboot的开发者来说&#xff0c;配置SMTP服务器来实现邮件发送并不是一件复杂的事情。AokSend将详细介绍如何通过配置SMTP服务器来实现Springboot邮件发送。 Springboot邮件发送&#x…

【SpringBoot】SpringBoot中的异常处理和异常跳转页面

目录 1.异常跳转页面 1.1 概念 1.2 使用 2.异常处理 2.1 概念 2.2 通过ExceptionHandler 注解处理异常&#xff08;局部处理&#xff09; 2.3 通过ControllerAdvice 注解处理异常&#xff08;全局处理&#xff09; 2.4 通过 SimpleMappingExceptionResolver 对象处理…

AI大模型达人秘籍:豆瓣9.2分推荐必读!

有很多程序员正在AIGC赛道中默默发财&#xff0c;有通过短视频做内容爆火&#xff0c;接广告的&#xff1b;有卖AI解决方案的&#xff1b;有卖AI课程的&#xff1b;也有卖AI产品&#xff0c;慢慢做大做强的…更不必说&#xff0c;那些拿下“人均年薪100万”大模型相关岗位的“赢…

多模态 AI 是零售业的未来吗?使用 GPT-4 Vision 和 MongoDB 矢量搜索探索智能产品发现

生成式人工智能如何重新定义零售盈利能力 欢迎来到雲闪世界。想象一下这样的购物体验&#xff1a;您上传了一张心仪服装或商品的照片。片刻之后&#xff0c;您便会收到来自您喜爱的商店的个性化、AI 驱动的类似商品推荐。这是一种革命性的零售体验&#xff0c;由一款创新应用实…

从零到一,数据恢复不求人!2024年四款全免费神器,轻松搞定

活在这个电脑手机满天飞的时代&#xff0c;我们天天跟数据打交道。工作文件、家庭照片、视频&#xff0c;这些都是我们的宝贝&#xff0c;一旦没了或者出问题&#xff0c;那可真够呛。好在我们有高科技帮忙&#xff0c;数据恢复现在也不是啥大问题。今儿个&#xff0c;我要给你…

数说故事 | 2024巴黎奥运会,“谷子文化”出圈了

全红婵金牌&#xff0c;全网沸腾。 摘金之后全妹的痛包&#xff08;itabag&#xff0c;是指挂满人物徽章和玩偶等周边的包包&#xff0c;因为这样的包会让人感觉“奇怪和夸张&#xff0c;日语的“痛”有此含义&#xff0c;所以被称为“痛包”&#xff09;&#xff0c;也让二次…

多模态大模型(MLLM):架构篇

**【导读】**多模态大模型主要是以LLM作为核心决策模块&#xff0c;主流架构有两种&#xff1a;LLM as Discrete Scheduler/Controller和LLM as joint part of system&#xff0c;第一种LLM充当任务调度的作用&#xff0c;第二种LLM通过Encoder-LLM-Decoder结构作为系统的关键连…

探索Python性能优化的神秘力量:Line Profiler

文章目录 探索Python性能优化的神秘力量&#xff1a;Line Profiler第一部分&#xff1a;背景第二部分&#xff1a;库简介第三部分&#xff1a;安装指南第四部分&#xff1a;基本使用方法第五部分&#xff1a;实际应用场景场景1&#xff1a;数据分析场景2&#xff1a;机器学习模…