Java:二叉树(1)

  从现在开始,我们进入二叉树的学习,二叉树是数据结构的重点部分,在了解这个结构之前,我们先来了解一下什么是树型结构吧!

一、树型结构

1、树型结构简介

  树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成的具有层次关系的集合。把它叫成树,是因为它看起来很像棵倒挂的树,也就是说,它是根朝上,叶朝下的。

  树具有以下几个特点:

1、有一个特殊的结点,称为根结点,它没有前驱结点。

2、除了根结点以外,其余结点被分为M(M>0)个互不相交的集合T1 、T2 、T3 ……Tm,其中每一个集合又是一棵类似的子树



2、树型结构的判断

如图:

1、A便是根结点,其余的B、C、D、E等都可以称为子树!

2、以此图,我们再引入两个概念:

父结点:有衍生出子类的结点称为父结点。比如A衍生出B、C、D,因此A是B、C、D结点的父结点!

子结点:父结点衍生出来的结点称为子结点。比如B、C、D都是A的子结点!

 注意:子树之间不能有交集。

子树之间有没有交集判断时有一个依据:  除了根结点以外,每个结点有且只有一个父结点!

分析此图:

1、C结点有 A 和 B两个父结点

2、结点有  D 和 H 两个父结点

因此,这个结构不是树型结构



3、树型结构的一些重要概念

1、结点的度:一个结点含有子树的个数称为该结点的度 ,如上图:A的度为4,D的度为2

2、树的度:一棵树中,所有结点度的最大值称为该树的度,如上图:该树的度为4

3、叶结点或终端结点:度为0的结点称为叶结点,如上图,E、F、G、H、J都称为叶结点

4、结点的层次:从根开始定义起,根为第一层,根的子结点为第二层,以此类推。

5、树的高度:即树中结点的最大层次,如图,该树的高度是3。

6、兄弟结点:拥有同一个父结点的称为兄弟结点,比如B、C、D、E互为兄弟结点。

7、堂兄弟结点:父结点在同一层次的称为堂兄弟结点,比如G和H互为堂兄弟结点。



二、二叉树 【概念】

1、什么是二叉树

二叉树是结点的一个有限集合,该集合

1、或者为空

2、或是是由一个根结点加上两棵分别称为左子树右子树的二叉树组成!

那为什么称为二叉树?

答:此树不存在度大于2的结点 。从上图可以看到,每个结点的度都不会超过2,因此它称为二叉树!



2、两种特殊的二叉树

1、满二叉树:

一颗二叉树,如果每层的结点数都达到最大值,则这棵树称为满二叉树,换言之,如果一棵二叉树的层数为K,且结点数是2^k -1,则它就是满二叉树!

如图:

2、完全二叉树:

对于层数为k的,结点数为n的二叉树,当且仅当其每一个结点都与层数为k的满二叉树中编号从0至n-1的结点一一对应时称之为完全二叉树

通俗点说,就是将结点从上到下,从左到右依次存放!

比如此图,它就是一个完全二叉树,它的每个结点都是按照右从上到下,从左到右依次存放!

举一个不是完全二叉树的反例:

那么,我们将其修改成完全二叉树,看看需要怎么操作?

只需要将给红色结点添加两个子结点,此时,这个又是一个完全二叉树!



3、二叉树的性质 

1、对于任何一棵二叉树,如果叶结点个数为n0,度为2的非叶结点个数为n2,则有n0=n2+1 

如果有兴趣的伙伴,我们可以来推导一下这个公式。

这个推导基于我们知道一个事实:一棵N个结点的树有N-1条边

推导过程:

假设二叉树度为0的结点个数为n0度为1的结点个数为n1度为2的结点个数为n2

那么我们可以知道,度为0的结点可以产生0条边,度为1的结点可以产生1条边,度为2的结点可以产生2条边!

就有:N-1=n1+n2+n2

           N=n0+n1+n2

将2式代入1式:n0+n1+n2-1=n1+n2+n2   ------>>   n0=n2+1


2、具有n个结点的完全二叉树的高度k为log2(n+1)上取整

如图,该完全二叉树有10个结点,log2(11)的结果是在3和4之间的,那么就向上取整,因此其高度是4!


3、对应具有n个结点的完全二叉树,如果按照从上至下,从左至右的顺序对所有结点从0开始编号,则对于序号为i的结点有:

》若i>0,父结点序号为:(i-1)/2  ;若i=0;无父结点:

如图:

》若2i+1<n,则下标为i的结点的左孩子序列为2i+1;否则就是无左孩子

》若2i+2<n,则下标为i的结点的右孩子序列为2i+2;否则就是无右孩子 

如图:


4、有奇数个结点的完全二叉树, 度为1的结点个数为0;有偶数个结点的完全二叉树,度为1的结点个数为1。



 三、二叉树的存储

1、二叉树的节点

二叉树的存储结构分为:顺序存储类似于链表的链式存储

现在我们不介绍顺序存储,我们先来学习链式存储

二叉树的链式存储是由一个一个的节点连接起来的,每个节点包含三个域。

class Node{
int val;      //数据域
Node left;    //左孩子的引用
Node right;   //右孩子的引用
}



 2、二叉树的遍历

  前面我们学习链表和顺序表的时候,其实本质上都是在遍历链表和顺序表,以此完成增删查改操作,由于二叉树是非线性结构,因此其遍历的方式比较不同,所有在实现二叉树之前,让我们先来了解一下二叉树的遍历方法! 

二叉树有以下四种遍历方法:

》前序遍历:  根、左、右(先遍历根节点,然后再遍历左子树,最后遍历右子树)

》中序遍历:  左、 根 、右(先遍历左子树,然后遍历根节点,最后遍历右子树)

》后序遍历:  左 、右 、根(先遍历左子树,然后遍历右子树,最后遍历根节点)

》层序遍历: 从上到下,从左到右  依次遍历

让我们以打印二叉树结点数据为例子,看看不同的遍历序列有什么不同:

前序遍历:根  左  右(先遍历根节点,然后再遍历左子树,最后遍历右子树)

注意:图中的数字代表二叉树遍历的前后顺序!蓝色箭头为即遍历路径无论使用何种遍历方式,遍历路径都是不变的!

前序遍历要求:无需遍历树的左右子树便可以打印该节点的内容;

听不懂也没有关系,只要我们结合下面三种遍历方式的不同便可以深刻的理解了!

进入A(打印A),把A作为根节点,再遍历节点A的左子树, 

进入B(打印B),把B看作新的根节点,再遍历B的左子树;

进入D(打印D),把D看作新的根节点,再遍历D的左子树;

发现D的左子树为null,回到D,再遍历D的右子树;

发现D的右子树为null,回到D;

(注意:此时完成了第一颗树D的遍历)

回到B,再遍历B的右子树;

发现B的右子树为null,回到B;

(注意:此时完成了第二棵树B的遍历)

回到A,再遍历A的右子树C;

进入C(打印C),把C看作新的根节点,再遍历C的左子树;

进入E(打印E),把E看作新的根节点,再遍历E的左子树,

发现E的左子树为null,回到E,再遍历E的右子树;

发现E的右子树为null,回到E;

(注意:此时完成了第三棵树E的遍历)

回到C,再遍历C的右子树F;

进入F(打印F),把F看作新的根节点,再遍历F的左子树;

发现F的左子树为null,回到F;再遍历F的右子树;

发现F的右子树为null,回到F;

(注意:此时完成了第四棵树F的遍历)

回到C;

(注意:此时完成了第五颗树C的遍历)

回到A;

(注意:此时全部的树都已经遍历完成)

因此,前序遍历打印的结果为:ABDCEF


中序遍历:  左、 根 、右(先遍历左子树,然后遍历根节点,最后遍历右子树)

中序遍历的时候,二叉树的遍历路径也是一样的,但是中序遍历要求:只有在完成每一棵树的左子树的遍历后,才可以打印该节点的内容;

比如,第一次进入A的时候,不能直接打印,要等A树的左子树遍历完成,再次回到A时才可以打印A。

进入A,把A作为根节点,再遍历A的左子树;

进入B,把B作为新的根节点,再遍历B的左子树;

进入D,把D作为新的根节点,再遍历D的左子树;

发现D的左子树为null,回到D,再遍历D的右子树;(此时D树的左子树遍历完成,可以打印D)

发现D的右子树为null,回到D;

(注意:此时完成第一棵树D的遍历)

回到B,然后再遍历B的右子树;(此时B树的左子树遍历完成,可以打印B)

发现B的右子树为null,回到B;

(注意:此时完成第二棵树B的遍历)

回到A,然后再遍历A的右子树C;(此时A树的左子树遍历完成,可以打印A)

进入C,把C看作新的根节点,再遍历C的左子树;

进入E,把E看作新的根节点,再遍历E的左子树;

发现E的左子树为null,回到E,再遍历E的右子树;(此时E树的左子树遍历完成,可以打印E)

发现E的右子树为null,回到E;

(注意:此时完成第三棵树E的遍历)

回到C,再遍历C的右子树F;(此时C树的左子树遍历完成,可以打印C)

进入F,把F看作新的根节点,再遍历F的左子树;

发现F的左子树为null,回到F,再遍历F的右子树;(此时F树的左子树遍历完成,可以打印F)

发现F的右子树为null,回到F;

(注意:此时完成第四棵树F的遍历)

回到C;

(注意:此时完成第五课树C的遍历)

回到A;

(注意:此时完成全部树的遍历)

因此,中序遍历打印的结果为:DBAECF


后序遍历:  左 、右 、根(先遍历左子树,然后遍历右子树,最后遍历根节点)

 后序遍历时,要求完成每一棵树的左右子树的遍历,才可以打印该节点的内容;

进入A,把A作为根节点,再遍历A的左子树;

进入B,把B作为新的根节点,再遍历B的左子树; 

进入D,把D作为新的根节点,再遍历D的左子树;

发现D的左子树为null,回到D,再遍历D的右子树;

发现D的右子树为null,回到D;(此时D树的左右子树遍历完成,可以打印D)

(注意,此时完成第一颗树D的遍历)

回到B,再遍历B的右子树;

发现B的右子树为null,回到B;(此时B树的左右子树遍历完成,可以打印B)

(注意,此时完成第二棵树B的遍历)

回到A,再遍历A的右子树C;

进入C,把C作为新的根节点,再遍历C的左子树;

进入E,把E作为新的根节点,再遍历E的左子树;

发现E的左子树为null,回到E,再遍历E的右子树;

发现E的右子树为null,回到E;(此时E树的左右子树遍历完成,可以打印E)

(注意:此时完成第三棵树E的遍历)

回到C,再遍历C的右子树;

进入F,把F作为新的根节点,再遍历F的左子树;

发现F的左子树为null,回到F,再遍历F的右子树;

发现F的右子树为null,回到F;(此时F树的左右子树遍历完成,可以打印F)

(注意:此时完成第四棵树F的遍历)

回到C;(此时C树的左右子树遍历完成,可以打印C)

(注意:此时完成第五颗树C的遍历)

回到A;(此时A树的左右子树的遍历完成,可以打印A)

(注意:此时完成全部树的遍历)

因此,后序遍历打印的结果为:DBEFCA

层序遍历的情况暂且不作讲解,大家有兴趣可以自行了解!



四、二叉树的实现

现在我们万事俱备,是时候来了解一下二叉树的实现了!

类文件,首先,我们先创建一个TestBinaryTree类,用来实现我们的二叉树;

这个Test类,用来测试我们的二叉树!

1、节点类:

通过前面的学习,我们知道,二叉树的每一个节点包含三个域:left、val、right;

因此,我们需要在TestBinaryTree类内部定义一个内部的节点类!

 

内部类的实现:

注意,这个Node类是定义在TestBinaryTree类的内部的!



2、创建一个简易的二叉树: 

在实现遍历方法之前,我们需要创建一个二叉树!这里我们不妨在TestBinaryTree类内部实现一个方法,创建一个如图的二叉树!

 //实现一个方法:该方法可以简易创建一个二叉树public Node createTree(){//实例化几个对象Node A=new Node('A');Node B=new Node('B');Node C=new Node('C');Node D=new Node('D');Node E=new Node('E');Node F=new Node('F');Node G=new Node('G');Node H=new Node('H');//连接这几个对象A.left=B;A.right=C;B.left=D;B.right=E;C.left=F;C.right=G;E.right=H;return A;}


3、前序遍历方法:  

//实现前序遍历:根、左、右public void preOrder(Node root){//如果root==null,返回if(root==null){return;}//先打印根节点内容System.out.print(root.val+" ");//再遍历左子树preOrder(root.left);//最后遍历右子树preOrder(root.right);}

这个方法是通过递归实现,因此我们还是举一个例子来让大家充分理解它的递归过程!

以如下图的二叉树为例子!

 



4、中序遍历方法:

//实现中序遍历:左、根、右public void inOrder(Node root){if(root==null){return;}//先遍历左子树inOrder(root.left);//再打印根节点内容System.out.print(root.val+" ");//最后遍历右子树inOrder(root.right);}


5、后序遍历方法:

 //实现后序遍历:左、右、根public void postOrder(Node root){if(root==null){return;}//先遍历左子树postOrder(root.left);//再遍历右子树postOrder(root.right);//最后打印根节点内容System.out.print(root.val+" ");}



6、遍历方法的运行结果



7、计算二叉树节点个数的方法:

首先,我们需要知道

树的节点个数=左子树的节点个数+右子树的节点个数+1 

这个1就是代表这棵树的根节点,在实际计算的过程当中,我们又可以将每一个节点看作一颗树,因此,这个方法我们使用递归实现!

//计算二叉树的节点个数public int size(Node root){if(root==null){return 0;}int ret=size(root.left)+size(root.right)+1;return ret;}


8、计算叶子节点的个数的方法:

这里我们需要弄清楚一个点,什么是叶子节点,叶子节点就是没有左右子树的节点!

计算的主体逻辑:叶子节点的个数=左子树叶子节点的个数+右子树叶子节点的个数

 //计算二叉树的叶子节点的个数public int getLeafNodeCount(Node root){if(root==null){return 0;}//叶子节点if(root.left==null&&root.right==null){return 1;}int ret=getLeafNodeCount(root.left)+getLeafNodeCount(root.right);return ret;}


9、计算第K层有几个节点的方法:

  //计算第K层有多少个节点public int getKLevelNodeCount(Node root,int k){if(k==0){return 0;}if(k==1){return 1;}return getKLevelNodeCount(root.left,k-1)+getKLevelNodeCount(root.right,k-1);}



10、计算整棵树的高度的方法:

整棵树的高度=左子树高度和右子树高度中的最大值

//计算二叉树的高度public int getHeight(Node root){if(root==null){return 0;}int leftHeight=getHeight(root.left);int rightHeight=getHeight(root.right);return leftHeight>rightHeight?leftHeight+1:rightHeight+1;}


11、查找某个值的方法:

 //找到值为val的节点public Node find(Node root,char val){if(root==null){return null;}if(root.val==val){return root;}Node nodeLeft=find(root.left,val);if(nodeLeft!=null){return nodeLeft;}Node nodeRight= find(root.right,val);if(nodeRight!=null){return nodeRight;}//找不到,返回nullreturn null;}

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

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

相关文章

Matlab无基础快速上手1(遗传算法框架)

本文用经典遗传算法框架模板&#xff0c;对matlab新手友好&#xff0c;快速上手看懂matlab代码&#xff0c;快速应用实践&#xff0c;源代码在文末给出。 基本原理&#xff1a; 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;是一种受生物学启发的优化算法…

在Gtiee搭建仓库传代码/多人开发/个人代码备份---git同步---TortoiseGit+TortoiseSVN

文章目录 前言1.安装必要软件2. Gitee建立新仓库git同步2.1 Gitee建立新仓库2.2 Gitee仓库基本配置2.3 Git方式进行同步 3. TortoiseGitTortoiseSVN常用开发方式3.1 秘钥相关3.2 TortoiseGit拉取代码TortoiseGit提交代码 4. 其他功能探索总结 前言 正常企业的大型项目都会使用…

TR5 - Transformer的位置编码

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目录 前言什么是位置编码1. 定义2. 三角函数3. 位置编码公式4. 位置编码示例 可视化理解位置编码1. 代码实现2. 观察不同位置对应的曲线3. 整句话的位置编码可…

排序 “贰” 之选择排序

目录 ​编辑 1. 选择排序基本思想 2. 直接选择排序 2.1 实现步骤 2.2 代码示例 2.3 直接选择排序的特性总结 3. 堆排序 3.1 实现步骤 3.2 代码示例 3.3 堆排序的特性总结 1. 选择排序基本思想 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个…

Guitar Pro简谱输入方法 Guitar Pro简谱音高怎么调整,Guitar Pro功能介绍

一、新版本特性概览 Guitar Pro v8.1.1 Build 17在保留了前版本强大功能的基础上&#xff0c;进一步优化了用户体验和功能性能。新版本主要更新包括以下几个方面&#xff1a; 界面优化&#xff1a;新版界面更加简洁美观&#xff0c;操作更加便捷&#xff0c;即使是初学者也能快…

在线拍卖系统,基于SpringBoot+Vue+MySql开发的在线拍卖系统设计和实现

目录 一. 系统介绍 二. 功能模块 2.1. 管理员功能模块 2.2. 用户功能模块 2.3. 前台首页功能模块 2.4. 部分代码实现 一. 系统介绍 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系…

Docker - 简介

原文地址&#xff0c;使用效果更佳&#xff01; Docker - 简介 | CoderMast编程桅杆https://www.codermast.com/dev-tools/docker/docker-introduce.html Docker是什么&#xff1f; Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 D…

vulfocus靶场couchdb 权限绕过 (CVE-2017-12635)

Apache CouchDB是一个开源数据库&#xff0c;专注于易用性和成为"完全拥抱web的数据库"。它是一个使用JSON作为存储格式&#xff0c;JavaScript作为查询语言&#xff0c;MapReduce和HTTP作为API的NoSQL数据库。应用广泛&#xff0c;如BBC用在其动态内容展示平台&…

串口RS485

1.原理 全双工&#xff1a;在同一时刻可以同时进行数据的接收和数据的发送&#xff0c;两者互不影响 半双工&#xff1a;在同一时刻只能进行数据的接收或者数据的发送&#xff0c;两者不能同时进行 差分信号幅值相同&#xff0c;相位相反&#xff0c;有更强的抗干扰能力。 干…

vlan的学习笔记1

vlan&#xff1a; 1.一般情况下:以下概念意思等同: 一个vlan一个广播域 一个网段 一个子网 2.一般情况下: &#xff08;1&#xff09;相同vlan之间可以直接通信&#xff0c;不同vlan之间不能直接通信! &#xff08;2&#xff09;vlan技术属于二层技术&…

C语言中, 文件包含处理,#include< > 与 #include ““的区别

文件包含处理 指一个源文件可以将另外一个文件的全部内容包含进来 &#xff23;语言提供了#include命令用来实现文件包含的操作 #include< > 与 #include ""的区别 <> 表示系统直接按系统指定的目录检索 "" 表示系统先在 "" 指定…

Rust序列化和反序列化

Rust 编写python 模块 必备库 docker 启动 nginx 服务 NGINX 反向代理配置

MySQL下载与安装

文章目录 1&#xff1a;MySQL下载与安装2&#xff1a;配置环境变量3&#xff1a;验证是否安装成功 1&#xff1a;MySQL下载与安装 打开MySQL官网&#xff0c;MySQL 下载链接选择合适的版本和操作系统&#xff0c;页面跳转之后选择No thanks, just start my download.等待下载即…

linux中/etc/hosts文件的内容和功能

更准确的说是主机和ip地址映射绑定配置文件 用于主机名解析成ip地址的 转换配置 效果&#xff1a; 这个东西是局域网下面的解析&#xff0c;老师说是本地局域网解析 windows对应的就是

笔试狂刷--Day2(模拟高精度算法)

大家好,我是LvZi,今天带来笔试狂刷--Day2(模拟高精度算法) 一.二进制求和 题目链接:二进制求和 分析: 代码实现: class Solution {public String addBinary(String a, String b) {int c1 a.length() - 1, c2 b.length() - 1, t 0;StringBuffer ret new StringBuffer()…

4.9 启动系统任务❤❤❤

有一些特殊的任务需要在系统启动时执行&#xff0c;例如配置文件加载、数据库初始化等操作。 Spring Boot对此提供了两种解决方案&#xff1a;CommandLineRunner和ApplicationRunner。 CommandLineRunner和ApplicationRunner基本一致&#xff0c;差别主要体现在参数上。 1. Co…

FastChat启动与部署通义千问大模型

FastChat简介 FastChat is an open platform for training, serving, and evaluating large language model based chatbots. FastChat powers Chatbot Arena, serving over 10 million chat requests for 70 LLMs.Chatbot Arena has collected over 500K human votes from sid…

[SWPUCTF 2022 新生赛]android

安卓题第一次写 先解压&#xff0c;改apk后缀为zip再次解压用dex2jar反编译得到jar文件&#xff0c;再用jd-gui查看即可得到flag

AI大模型日报#0418:Stable Diffusion 3开放API、Meta新研究让AI Agent理解物理世界

导读&#xff1a; 欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了每条资讯的摘要。标题: 微软刚发布了VASA-1 这个人工智能可以让单张图像具有生动的说话和歌唱能力 摘要: 微软发布了VASA-1人工智能&#xff0c;它能使单…

Webstorm 24.1 复制、剪切卡死问题官方回复

Webstorm 24.1 复制、剪切卡死问题官方回复 https://youtrack.jetbrains.com/issue/WEB-65787/WebStorm-2024.1-UI-was-frozen-for-N-ms-after-copy-paste 2024-04-20 更新&#xff1a; UI 卡死问题已于 2024-04-20。发布的 24.1.1 版本解决