资料来源关于二叉树,你该了解这些!| 二叉树理论基础一网打尽,二叉树的种类、二叉树的存储方式、二叉树节点定义、二叉树的遍历顺序_哔哩哔哩_bilibili
二叉树的种类
满二叉树:
除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。国内教程定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的深度为K,且结点总数是(2^k) -1 ,则它就是满二叉树
节点数量 2的k次方-1 k为层数
完全二叉树:
除了底层之外
其他层都是满的
底层不一定满,但从左到右一定连续
是
不是 底层断开了
满二叉树一定是完全二叉树
二叉搜索树,节点便于搜索,搜索一个节点的时间复杂度O(logN)级别
它对节点结构没要求,对节点顺序有要求
左子树所有节点都小于中间节点
右子树所有节点都大于中间节点
平衡二叉搜索树
平衡:左右子树高度绝对值不高于1
二叉树的存储方式
链式存储和线性存储
链式:
线性
arr[a,b,c,d,e,f,g]
下标0,1,2,3,4,5,6
通过当下下标可以查询它对应的左右叶子
如a,下标0 对应左右叶子节点公式为 2*下标i +1/ +2
a(0) 的左右叶子就是 b(1) c(2)
比如b(1)呢 对应的
2*1+1 = d(3) 2*1+2 = e(4)
一般用链式存储
二叉树的遍历
类似于图论的深度优先搜索和广度优先搜索
深度优先搜索:前中后序遍历(递归法-栈 先进后出,后进先出)
广度优先搜索: 层序遍历(迭代法-队列 先进先出)
前中后序遍历:
前序遍历 中左右
中序遍历 左中右
后序遍历 左右中
这个前中后可以当作描述 中这个位置
定义方式
二叉树的遍历方式(CV自代码随想录二叉树理论基础)
关于二叉树的遍历方式,要知道二叉树遍历的基本方式都有哪些。
一些同学用做了很多二叉树的题目了,可能知道前中后序遍历,可能知道层序遍历,但是却没有框架。
我这里把二叉树的几种遍历方式列出来,大家就可以一一串起来了。
二叉树主要有两种遍历方式:
- 深度优先遍历:先往深走,遇到叶子节点再往回走。
- 广度优先遍历:一层一层的去遍历。
这两种遍历是图论中最基本的两种遍历方式,后面在介绍图论的时候 还会介绍到。
那么从深度优先遍历和广度优先遍历进一步拓展,才有如下遍历方式:
- 深度优先遍历
- 前序遍历(递归法,迭代法)
- 中序遍历(递归法,迭代法)
- 后序遍历(递归法,迭代法)
- 广度优先遍历
- 层次遍历(迭代法)
在深度优先遍历中:有三个顺序,前中后序遍历, 有同学总分不清这三个顺序,经常搞混,我这里教大家一个技巧。
这里前中后,其实指的就是中间节点的遍历顺序,只要大家记住 前中后序指的就是中间节点的位置就可以了。
看如下中间节点的顺序,就可以发现,中间节点的顺序就是所谓的遍历方式
- 前序遍历:中左右
- 中序遍历:左中右
- 后序遍历:左右中
大家可以对着如下图,看看自己理解的前后中序有没有问题。
最后再说一说二叉树中深度优先和广度优先遍历实现方式,我们做二叉树相关题目,经常会使用递归的方式来实现深度优先遍历,也就是实现前中后序遍历,使用递归是比较方便的。
之前我们讲栈与队列的时候,就说过栈其实就是递归的一种实现结构,也就说前中后序遍历的逻辑其实都是可以借助栈使用非递归的方式来实现的。
而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
这里其实我们又了解了栈与队列的一个应用场景了。
具体的实现我们后面都会讲的,这里大家先要清楚这些理论基础。
递归方式
三部曲
递归遍历框架
二叉树的递归遍历前中后序如图
所谓前中后序,就是先赋值的情况,前序很明显是中左右的顺序。
递归参数和返回值,参数是节点和list集合,因为list是一个全局对象所以没返回值。
终止条件是当前节点为空,
单层递归的逻辑是赋值,左节点递归,右节点递归。
中、后序交换后面的顺序即可。
前中后序还可以使用非递归的方式,因为我们的java也是使用的栈这个结构进行递归处理,参数,局部变量就行弹栈压栈。我们可以手动用一个栈模拟递归。
迭代法-前序
list模拟栈
result接受数据
总节点入栈,然后进入循环,
在栈不为空的情况下
中节点入值
左右节点不为空的情况下,再入栈,而因为是栈先进后出,我们就以右左的形式,保证出栈是左右的形式。
list为空 结束循环
迭代法-后序
后序遍历是 左右中的形式,我们可以实现一个中右左的出栈入值情况,然后翻转结果集即可。
迭代法-中序
前面可以发现前序后续代码结构清晰和类似,那是因为节点的访问,和数据的存入是同一个节点
而中序遍历——左中右的格式,很明显节点只能一个个访问,而存入是到叶子节点了才开始存入。
所以我们需要一个指针来记录当前节点。
前面的为null判断是,如果父节点都为空 就直接返回 结束
后面的循环逻辑如下
当前节点不为null,或者当前集合不为空 就进入循环
节点不为空,
栈加入当前节点
指针指向当前节点的左节点
节点为空
弹栈,指针指向左节点不存在的父节点 即上述的当前节点
结果集加入当前节点值,
指针指向当前节点的右节点。
就是说指针想一直向左,左没了,就把栈里弹出来,是当前左节点为空的父节点。再把父节点的值加入结果集,指针再改为右节点。
确定左节点为空,弹出父节点,赋值,再指针移向右节点。模拟左中右的遍历顺序。
如下例子:
我们int i 为记录循环里的操作次数。
指针不为空,进循环,进栈3,指针指向3的左节点1。
指针不为空,进循环,进栈1,指针指向1的左null。
指针为空,但list不为空,进入循环。指针为空,弹栈1,录入结果集1,指针指向1右null。
指针为空,但list不为空,进入循环。指针为空,弹栈3,录入结果集3,指针指向3的右节点2。
指针不为空,进循环,进栈2,指针指向2的左null。
指针为空,但list不为空,进入循环。指针为空,弹栈2,录入结果集2,指针指向2右null。
指针不为空,list为空,不进入循环。
可见结果集,和循环次数6。
二叉树的层序遍历
使用一个队列,迭代法,对应图论的广度优先搜索。
先把头节点存入
while循环 list不为空{
新建一个结果集
记录当前list的大小
按照大小进行循环
{
取list的头部节点
在判断这个节点的左右节点存在吗
存在就分别加入list尾部
}
更新结果集
}