再识二叉树

news2024/12/24 2:23:00

1. 二叉树的存储

        二叉树的存储结构分为:顺序存储和类似于链表的链式存储。 其中二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有二叉和三叉表示方式(这里本主主要讲的是链式存储),具体代码如下:

        二叉表示:

// 孩子表示法
class Node {
	int val; // 数据域
	Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
	Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}

        三叉表示: 

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

         注意:本主本篇创建的二叉树都是基于二叉表示的;

2、二叉树的遍历

2.1 通过代码了解二叉树

        在学习二叉树的基本操作前,首先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习;

        通过以下代码我们可以构造一个简单的二叉树。

import com.sun.org.apache.regexp.internal.RE;

import java.util.concurrent.Callable;

public class BinaryTree {
    static class TreeNode{
        public char value;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(char value){
            this.value = value;
        }
    }
    //创建一棵二叉树 创建成功后 返回根节点A
    public TreeNode createTree(){
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        C.right = G;
        E.right = H;
        return  A;
    }
}

        下图就是我们代码构造的二叉树图; 

                       

        注意:上述代码并不是创建二叉树的方式,而是我们用代码讲二叉树详细的描述出来;我们通过前一张的内容学习知道二叉树是递归式的(整个树从下到上,从左到右,每一个左节点和右节点与根节点三方面构成一个小树,由此可知每一个左子树和右子树与根节点构成二叉树),因此后序二叉树的基本操作中基本都是按照该递归概念实现的。

2.2 二叉树的遍历详解

        所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结 点均做一次且仅做一次访问

依次对树中每个结点有且仅做一次访问)到一个节点并不一定要遍历该节点;

        在遍历二叉树时,如果没有进行某种约定,每个人都按照自己的方式遍历,得出的结果就比较混乱,如果按照某种规则进行约定,则每个人对于同一棵树的遍历结果肯定是相同的。如果N代表根节点,L代表根节点的左子树,R代表根节点的右子树,则根据遍历根节点的先后次序有以下遍历方式:

NLR:前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点—>根的左子树—>根的右子树。

LNR:中序遍历(Inorder Traversal)——根的左子树—>根节点—>根的右子树。

LRN:后序遍历(Postorder Traversal)——根的左子树—>根的右子树—>根节点

        如下图的二叉树通过详细的递归的思路进行遍历,按照不同的遍历方式我们可以得到不同的访问结果,其二叉树如下图所示:

        以下是该二叉树的三种遍历的结果: 

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

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

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

        我们接下来详细的分析一下三种遍历的过程;

2.2.1 前序遍历

1.从1节点开始,遍历1结点--->

2. 递归到1结点的左子树,遍历2节点--->

3. 递归到2结点的左子树,遍历3节点--->

4. 递归到3结点的左子树,该节点为null,回退到3节点--->

5. 递归到3结点的右子树,该节点为null,回退到3节点,3节点已经被遍历,回退到2节点--->

6. 递归到2结点的右子树,该节点为null,回退到2节点,2节点已经被遍历,回退到1节点,1节点已经被遍历--->

7. 递归到1结点的右子树,遍历4节点--->

8. 递归到4结点的左子树,遍历5节点--->

9. 递归到5结点的左子树,该节点为null,回退到5节点,5节点已经被遍历,回退到4节点--->

10. 递归到4结点的右子树,遍历6节点--->

11. 递归到6结点的左子树,该节点为null,回退到6节点--->

12. 递归到6结点的右子树,该节点为null,此刻6节点是整个树的最右节点,此时遍历结束

2.2.2 中序遍历

1.从1节点开始--->

2. 递归到1结点的左子树--->

3. 递归到2结点的左子树--->

4. 递归到3结点的左子树,该节点为null,回退到3节点,此时遍历3节点--->

5. 递归到3结点的右子树,该节点为null,回退到3节点,3节点已经被遍历,回退到2节点,此时遍历2节点--->

6. 递归到2结点的右子树,该节点为null,回退到2节点,2节点已经被遍历,回退到1节点,此时遍历1节点--->

7. 递归到1结点的右子树,到达4节点--->

8. 递归到4结点的左子树,到达5节点--->

9. 递归到5结点的左子树,该节点为null,回退到5节点,此时遍历5节点-->

10.递归到5节点的右子树,该节点为空,回退到5节点,其次回退到4节点,此时遍历4节点--->

12. 递归到4结点的右子树,到达6节点--->

12. 递归到6结点的左子树,该节点为null,回退到6节点,此时遍历6节点--->

13. 递归到6结点的右子树,该节点为null,此刻6节点是整个树的最右节点,此时遍历结束

2.2.3 后序遍历

1.从1节点开始--->

2. 递归到1结点的左子树--->

3. 递归到2结点的左子树--->

4. 递归到3结点的左子树,该节点为null,回退到3节点--->

5. 递归到3结点的右子树,该节点为null,回退到3节点,此时遍历3节点,回退到2节点--->

6. 递归到2结点的右子树,该节点为null,回退到2节点,此时遍历2节点,回退到1节点--->

7. 递归到1结点的右子树,到达4节点--->

8. 递归到4结点的左子树,到达5节点--->

9. 递归到5结点的左子树,该节点为null,回退到5节点-->

10.递归到5节点的右子树,该节点为空,回退到5节点,此时遍历5节点;其次回退到4节点,--->

12. 递归到4结点的右子树,到达6节点--->

12. 递归到6结点的左子树,该节点为null,--->

13. 递归到6结点的右子树,该节点为null,回退到6节点,此时遍历6节点;

14其次回退到4节点,此时遍历4节点-->

15其次回退到1节点,此时遍历1节点-->此刻1节点是整个树的根点,此时遍历结束

2.2.4 层序遍历

        层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层 上的节点,接着是第三层的节点,以此类推,自上而下,自左至右,逐层访问树的结点的过程就是层序遍历,其中图解如下;

                                       

2.3 前中后序代码实现(递归)

        思路:

        我们将一个大的二叉树分解为一个小二叉树(由一个根节点、一个左节点、右节点构成),通过递归的思想从根节点开始依次遍历,当递归的节点为空时,我们依次按顺序退回到之前的节点,并按照不同的遍历(按照当前节点在整个树的位置)规则打印当前的节点;

                                     

2.3.1 前序遍历

// 前序遍历  根   左子树  右子树   递归
    public void preOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        System.out.print(root.val+" ");
        preOrder(root.left);
        preOrder(root.right);
    }

        代码图解如下图所示,后面两个遍历不在画,略

2.3.2 中序遍历

    // 中序遍历
    public void inOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }

2.3.3 后序遍历

    // 后序遍历
    public void postOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val+" ");

    }

        总结:给出前序遍历与中序遍历给出后序遍历与中序遍历可以确定一个二叉树,但是不给中序遍历或者只给一个中序遍历,是无法确定一个二叉树的

        整体代码:

import com.sun.org.apache.regexp.internal.RE;

import java.util.concurrent.Callable;

public class BinaryTree {
    static class TreeNode{
        public char value;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(char value){
            this.value = value;
        }
    }
    //创建一棵二叉树 创建成功后 返回根节点A
    public TreeNode createTree(){
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        C.right = G;
        E.right = H;
        return  A;
    }

    // 前序遍历  根   左子树  右子树   递归
    public void preOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        System.out.print(root.value+" ");
        preOrder(root.left);
        preOrder(root.right);
    }

    // 中序遍历
    public void inOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.value+" ");
        inOrder(root.right);
    }

    // 后序遍历
    public void postOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.value+" ");
    }

    public static void main(String[] args) {
        BinaryTree binaryTree = new BinaryTree();
        BinaryTree.TreeNode root = binaryTree.createTree();
        binaryTree.preOrder(root);
        System.out.println();
        binaryTree.inOrder(root);
        System.out.println();
        binaryTree.postOrder(root);

    }



}

        测试结果:

 

3.二叉树的基本操作

        以下的多种操作都使用的是递归思想;

// 获取树中节点的个数
int size ( Node root );
// 获取叶子节点的个数
int getLeafNodeCount ( Node root );
// 获取第 K 层节点的个数
int getKLevelNodeCount ( Node root , int k );
// 获取二叉树的高度
int getHeight ( Node root );
// 检测值为 value 的元素是否存在
Node fifind ( Node root , int val ); 

3.1获取叶子节点的个数

        思路:

        当前节点的左右子树若都为空,说明该节点为叶子结点,返回1;树的叶子节点的个数=左树叶子节点的个数+右树叶子节点的个数

    int getLeafNodeCount(TreeNode root){
        if(root==null){
            return 0;
        }
        if(root.left==null&&root.right==null){
        return 1; 
        }
        return getLeafNodeCount(root.left)+getLeafNodeCount(root.right);
    }

3.2获取树中节点的个数 

        思路:

        若当前结点为空,返回0;先获取左节点个数,再获取右节点个数,然后返回两者相加再加上根节点的个数1

  int size(TreeNode root){
        if(root==null){
            return 0;
        }
        return size(root.right)+size(root.left)+1;
    }

3.3获取第K层节点的个数

        思路:

        依旧利用递归的思想,从树的根节点开始,每进入到树的下一层一次,令层数K-1,当k=1时,我们到达了我们要求的第k层,这样在进入到每一个下一层时,若当前节点不为空则返回1

,若为空则返回0;

        先遍历左子树k层结点,再遍历右子树k层结点,最后左子树结点加上右子树结点,就是该层结点总数

    int getKLevelNodeCount(TreeNode root, int k) {
        if(root==null){
            return 0;
        }
        if(k==1){
            return 1;
        }
        return getKLevelNodeCount(root.left,k-1)
                +getKLevelNodeCount(root.right,k-1);
    }

3.4获取二叉树的高度

        思路:

        分别统计左右子树的高度,然后进行比较,返回高度高的子树并加上根节点1;

    int getHeight(TreeNode root) {
        if(root==null){
            return 0;
        }
        int lefthight=getHeight(root.left);
        int rifhthight=getHeight(root.right);
        return lefthight>rifhthight?(lefthight+1):(rifhthight+1);
    }

3.5检测值为value的元素是否存在

        思路:

        依旧利用递归的思想,先遍历左子树,若没有找到,则返回null;若返回不为null,则返回该结点

        若左子树没有,则遍历右子树,道理相同,若最后都没找到,则返回null;

   TreeNode fifind(TreeNode root, int val) {
        if (root==null){
            return null;
        }
        if(root.val==val){
            return root;
        }
        TreeNode lefttree=fifind(root.left, val);
        if(lefttree!=null){
            return lefttree;
        }
        TreeNode righttree=fifind(root.right, val);
        if(righttree!=null){
            return righttree;
        }
        return null;
    }

ps:本次学习就到这里了,喜欢的话就请一键三连吧。 

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

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

相关文章

2个月拿下信息系统项目管理师攻略(攻略超级全)

信息系统项目管理师(高项)一次性过啦!结合这次备考经验,给大家总结一下复习方法。 先上图,开心一下! 一、我为什么选择了高项 为什么我会选信息系统项目管理师,也就是我们常说的高项。 原因1…

bpftrace原理与使用方法

Bpftrace 概念和原理bpftrace安装bpftrace 语法结构bpftrace 变量内置变量自定义变量Map变量 内置函数Bpftrace操作案例文件系统磁盘进程内存 bpftrace是一种基于eBPF(Extended Berkeley Packet Filter)的跟踪工具,用于在Linux系统中进行动态…

PostGIS学习教程十一:投影数据

PostGIS学习教程十一:投影数据 地球不是平的,也没有简单的方法把它放在一张平面纸地图上(或电脑屏幕上),所以人们想出了各种巧妙的解决方案(投影)。 每种投影方案都有优点和缺点,一…

有了安卓模拟器,就能在Windows 10或11上像使用安卓操作系统一样使用安卓

你可以使用Android模拟器在Windows 11或Windows 10中运行Android应用程序。如果你喜欢的应用程序只在手机上运行,但你想在电脑上使用,这些模拟器会很有用。 BlueStacks 与整个操作系统模拟器不同,BlueStacks只在Windows上模拟Android应用程序。它真的很容易使用,所以你不需…

鸿蒙OS应用开发的开发环境

鸿蒙OS应用开发的开发环境 鸿蒙系统发展越来越快,已经开始走进千家万户,从手机到电视机,再到汽车,以后各种手表、智能设备等等。这已经是一个广泛应用的操作系统,也是跟大家生活密切相关的操作系统。要想在这个平台上…

人脸识别安卓主板_MTK方案智能闸机门禁工业安卓主板定制开发

人脸识别主板广泛应用于各个领域,包括人脸支付系统、人脸识别监控系统、写字楼办公楼门禁闸机、校园、地铁、住宅门禁、考勤机、智能门锁、广告机、售卖机等。 主板基于联发科MTK方案,并由行业PCBA和MTK的核心板组成。根据产品需求,可以选择…

SpringBoot3.x代码生成器构建的三层架构主启动类报错

【场景复现】 jdk21环境变量springboot3.x、mybatisplus generator3.5.3构建工程启动: 【原因分析】 显示 factoryBeanObjectType 属性的值类型不正确,应该是一个实现了 FactoryBean 接口的类的全限定名。 注解注入每层对象,找不到Factory…

嵌入式系统

嵌入式系统 目前国内一个普遍认同的嵌入式系统定义是:以应用为中心、以计算机技术为基础,软件硬件可裁剪,适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。(引用自《嵌入式系统设计师教程》) …

node后端接口无法插入数据为emoji的表情的问题

原因 emoji的表情一般是这样的\xF0\x9F\x98\x80或者是\xF0\x9F\x98 ,事实上 一般数据库的utf8的编码类型都是能保存\xF0\x9F\x98 但是不能保存\xF0\x9F\x98\x80这种样的emoji,要将数据库编码格式为utf8mb4 也就是utf8的超集 另外,除了 数据库…

论文精读 MOG2 阴影检测

An Improved Adaptive Background Mixture Model for Real-time Tracking with Shadow Detection 一种用于阴影检测实时跟踪的改进自适应背景混合模型 承接上一篇博客:论文精读 && MOG && 埃里克格里姆森-CSDN博客 目录 一、摘要 二、结论 三…

matplot函数调整子图大小测试

调整subplot()函数的子图间距 import numpy as np import matplotlib.pyplot as plt for i in range(1,7):figsize 10,6plt.subplot(2,3,i)plt.text(0.5,0.5,str((2,3,i)),fontsize18,hacenter) **plt.subplots_adjust(hspace3.3, wspace0.3)** plt.show()import numpy as np…

低代码/无代码应用赋能数字化

目录 一、低代码喧嚣甚上 二、低代码平台适用于哪些应用? 三、低代码与IT开发 (1)IT开发: (2)低代码开发: 四、最后 一、低代码喧嚣甚上 随着数字化转型的深入,越来越多的企业开始寻…

画好一张规范的原理图,这些点你可要注意了!

不光是代码有可读性的说法,原理图也有。很多时候原理图不仅仅是给自己看的,也会给其它人看,如果可读性差,会带来一系列沟通问题。所以,要养成良好习惯,做个规范的原理图。此外,一个优秀的原理图…

结构体,自定义类型

目录 结构体 结构体的声明 结构体的自引用 结构体的定义和初始化 结构体内存对齐 ​编辑 结构体的对齐规则: 为什么存在内存对齐? 修改默认对齐数 结构体传参 位段 什么是位段 位段的内存分配 位段的跨平台问题 枚举 联合(共用体…

vivado sdk mem超出

Description Resource Path Location Type region microblaze_0_local_memory_ilmb_bram_if_cntlr_Mem_microblaze_0_local_memory_dlmb_bram_if_cntlr_Mem’ overflowed by 4288 bytes uart C/C Problem 问题的产生:fpga使用了microblaze搭建了一个soc系统&#…

SRC挖掘漏洞XSS

Markdown是一种轻量级标记语言,创始人为约翰格鲁伯(John Gruber)。它允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的 XHTML(或者HTML)文档。这种语言吸收了很多在电子邮件中已有的纯文本标记的…

ESP32-Web-Server编程-通过 Base64 编码在网页中插入图片

ESP32-Web-Server编程-通过 Base64 编码在网页中插入图片 概述 不同于上节 ESP32-Web-Server编程-在网页中通过 src 直接插入图片,本节引入 Base64 编码来显示图片。 Base64 是一种用64个字符来编码表示任意二进制数据的方法。任何符号都可以转换成 Base64 字符集…

VMware提示:此虚拟机似乎正在使用中,取得该虚拟机的所有权失败错误的解决方案

当你遇到这个的时候是不是很疑惑,现在给你解决方案 step1: 先找到配置文件目录 D:\centOs7_mini\ 这里写成你的这个 step2: 在这个地方查找最后面是 .vmx.lck文件夹,然后进行修改、删除、移动都可以 step3: 去虚拟机那边重新启动就行

【C语言快速学习基础篇】之一基础类型、进制转换、数据位宽

文章目录 一、基础类型(根据系统不同占用字节数会有变化)1.1、有符号整形1.2、无符号整形1.3、字符型1.4、浮点型1.5、布尔型 二、进制转换2.1、二进制2.2、八进制2.3、十进制2.4、十六进制2.5、N进制2.6、进制转换关系对应表 三、数据位宽3.1、位3.2、字节3.3、字3.4、双字3.5…

2023年快结束了,来看看你使用的编程语言在排行榜上排名第几?

【关注微信公众号:跟强哥学SQL,回复“笔试”免费领取大厂SQL笔试题。】 废话不多说,直接上图(双击图片查看大图): 这张编程语言排行榜来源于tiobe.com网站,根据TIOBE编程社区指数进行排名&…