把温柔和例外留给值得的人:比如你
大家好,这里是新一,请多关照🙈🙉🙊。在本篇博客中,新一将会为大家介绍数据结构与算法之二叉树基础篇,我们知道二叉树在面试的时候出现频率是极高的,所以搞懂二叉树也成了学好数据结构的必经之路,所以为了方便大家理解,新一特地给大家附上了 源码和图片 便于大家理解,干货满满哟。(以下结果均在IDEA中编译)希望在方便自己复习的同时也能帮助到大家。😜😜😜🎪🚀🧰
以下是我们的文章
文章目录
一.🎪 树的基本结构
树是一种非线性的数据结构操作,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
1.1 🚀 树的特点
树具有以下特点:
● 有一个特殊的结点,称为根结点,根结点没有前驱结点。
● 除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、…、Tm,其中每一个集合Ti (1 <= i <=m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。
● 树是 递归 定义的。
● 树形结构中,子树之间不能有交集, 否则就不是树形结构
那么树与非树的区别呢?
1.2 🚀 树的要素
二叉树概念 | 解释 |
---|---|
节点的度 | 一个结点含有子树的个数称为该结点的度; 如上图:A的度为6 |
树的度 | 一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6 |
叶子结点或终端结点 | 度为0的结点称为叶结点; 如上图:B、C、H、I...等节点为叶结点 |
双亲结点或父结点 | 若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点 |
孩子结点或子结点 | 一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点 |
根结点 | 一棵树中,没有双亲结点的结点;如上图:A |
结点的层次 | 从根开始定义起,根为第1层,根的子结点为第2层,以此类推 |
树的高度或深度 | 树中结点的最大层次; 如上图:树的高度为4 |
非终端结点或分支结点 | 具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点 |
堂兄弟结点 | 双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点 |
结点的祖先 | 从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先 |
子孙 | 以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙 |
森林 | 由m(m>=0)棵互不相交的树组成的集合称为森林 |
1.3 🚀 树的表现形式
树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法,孩子表示法、孩子双亲表示法、孩子兄弟表示法等等。新一这里给大家介绍孩子兄弟表示法。
class BTNode3 {
private char val;//数据域
private BTNode3 firstChild;//第一个孩子节点
private BTNode3 nextBrother;//从该节点往右第一个兄弟节点
}
听不懂?没关系,那下面这个还扛得住吗?🏀🏀🏀
1.4 🚀 树的应用
其实在不知不觉之间树已经深入我们的生活,就比如我们的任务管理器即是一种树形结构:
二.🎪 二叉树
一棵二叉树是结点的一个有限
集合,该集合:要么为空,要么是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
2.1 🚀 二叉树的特点
二叉树结构:
1.不存在度大于2的结点
2.它的子树有左右之分,次序不能颠倒,因此二叉树是有序树
二叉树有以下几种形态:
注意:即便它是空树或者只有根,它也是二叉树
2.2 🚀 满二叉树与完全二叉树
这两种二叉树非常重要,不管是在做题还是在面试中。
满二叉树
一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵二叉树的层数为K,且结点总数是 2 ^ k - 1,则它就是满二叉树。
完全二叉树
完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从0至n-1的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树
2.3 🚀 二叉树的性质*
以下性质非常重要,请友友们牢记:
1. 若规定根结点的层数为1,则一棵非空二叉树的第i
层上最多有 2^(i-1)
个结点
2. 若规定只有根结点的二叉树的深度为1,则深度为K
的二叉树的最大结点数是 2^K - 1
(k>=0)
当深度为K的二叉树最大节点数即为全部放满——满二叉树,把各层最大数相加即可,等比数列求和:2^0 + 2^1 + 2^2 + ...... + 2^(k-1)
= 2^k - 1
3. 对任何一棵二叉树, 如果其叶结点个数为 n0
, 度为2的非叶结点个数为 n2
,则有n0=n2+1
。
对于任意一颗二叉树我们知道它的节点总数 n = n0 + n1 + n2
,而它的总边数为n - 1
,因为相当于一个子节点一条边,根节点没有边,一个度为2的父节点产生2条边,度为1的父节点产生一条边,叶子节点作为父节点时不产生边,故我们得到以下式子:
n = n0 + n1 + n2
n - 1 = 0 * n0 + 1 * n1 + 2 * n2
两式结合即可以得到 n0=n2+1
5. 具有n
个结点的完全二叉树的深度k
为 log2(n + 1)
(以2为底的对数)上取整
先拿一颗满二叉树来举例:我们知道对于它有 2 ^ k - 1 = n
,由此可得k = log2(n + 1)
6. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
若2i+1<n,左孩子序号:2i+1,否则无左孩子
若2i+2<n,右孩子序号:2i+2,否则无右孩子
🧰练习
答案:1.B 2.A 3.B 4.B
相信不少小伙伴对第二题有所疑惑吧,不慌我们来看看这棵完全二叉树
我们发现对于任何一棵完全二叉树来说:
偶数个节点: n1 = 1
奇数个节点: n1 = 0
故第二题:2n = n0 + n1 + n2 = n0 + 1 + n0 - 1 = 2 * n0 = 2n
2.4 🚀 二叉树的存储
二叉树的存储结构分为:顺序存储和类似于链表的链式存储,这里新一先给大家介绍链式存储
二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有孩子表示法和孩子双亲表示法
表示方式,具体如下:
class BTNode1{
int val;
private BTNode1 left;//左孩子引用
private BTNode1 right;//右孩子引用
//做题以及面试常考
}
class BTNode2{
int val;
private BTNode2 left;//左孩子引用
private BTNode2 right;//右孩子引用
private BTNode2 parent;//双亲 - 该节点的根节点
//AVL + 红黑 + B树会用
}
下面我们二叉树的遍历主要用的是孩子表示法
三.🎪 二叉树的遍历
二叉树的遍历十分重要,可以说大部分二叉树的题都要通过二叉树的遍历从而解答,二叉树的遍历一共有四种:前序遍历,中序遍历,后序遍历,层序遍历
3.1 🚀 前序遍历
前序遍历:访问根结点—>根的左子树—>根的右子树
中序遍历跟后序遍历跟前序遍历那里类似,不过要记住:无论是那种遍历,所有二叉树的子二叉树都要遵循其原则,二叉树的遍历是要通过递归来实现的,见下:
//前序遍历
void preOrder(TreeNode root) {//传入二叉树的根
if (root == null){//判断根是否为空
return;
}
System.out.print(root.val + " ");//打印节点信息
preOrder(root.left);//递归遍历该根节点的左子树
preOrder(root.right);//递归遍历该根节点的右子树
}
前序遍历结果:1 2 3 4 5 6
3.2 🚀 中序遍历
中序遍历:根的左子树—>根节点—>根的右子树。
//中序遍历
void inOrder(TreeNode root){//传入二叉树的根
if (root == null){//判断根是否为空
return;
}
inOrder(root.left);//递归遍历该根节点的左子树
System.out.print(root.val + " ");//中序打印根节点
inOrder(root.right);//递归遍历该根节点的右子树
}
中序遍历结果:3 2 1 5 4 6
3.3 🚀 后序遍历
后序遍历:根的左子树—>根的右子树—>根节点。
void postOrder(TreeNode root){//传入二叉树的根
if (root == null){//判断根是否为空
return;
}
postOrder(root.left);//递归遍历该根节点的左子树
postOrder(root.right);//递归遍历该根节点的右子树
System.out.print(root.val + " ");//后序打印根节点
}
后序遍历结果:3 1 5 6 4 1
3.4 🚀 层序遍历
层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
🧰练习
答案:1.A 2.A 3.D 4.A
总结下来这几道题就一个思路:先根据前序遍历或者后序遍历找到根,在通过这个根在中序遍历中找到其左子树和右子树,再通过前序或者后序找到子树的根,如此循环即可画出对应的二叉树
值得注意的是:只知道前序和后续遍历无法唯一确定二叉树,除此之外,知道这三个其中任意两个即可知道唯一确定的二叉树
💕家人们,学到这里我们的数据结构与算法中的e二叉树基础已经彻底弄懂啦,但这只是二叉树结构中很基础的一部分,后续新一还会继续更新二叉树有关内容,包括创建二叉树等方法的实现以及典型例题,如果觉得新一讲得还清楚的话,可以点个赞支持一下哦🥳🥳🥳,后续新一会持续更新JAVA的有关内容,学习永无止境,技术宅,拯救世界!