注意:
子树也必须是二叉树才能满足该树整体是一个二叉树
2.3.1 满二叉树
满二叉树: 一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树。
性质: 如果一个二叉树的层数是k,且节点数是 2k-1,则它就是满二叉树。
2.3.2 完全二叉树
完全二叉树: 完全二叉树是效率很高的数据结构,它是由满二叉树引申出来的。它的叶子节点只会出现在最后2层,且最后一层的叶子节点都靠左对齐。 (满二叉树是一种特殊的完全二叉树)
- 若规定根节点的层数为1,则一棵非空二叉树的第 i 层上最多有 2i-1(i>0)个节点
- 若规定只有根节点的二叉树的深度为1,则深度为 k 的二叉树的最大节点数是 2k-1(k>=0)
- 对任何一棵二叉树,如果其叶子节点个数为 m,度为2的非叶子节点个数为 n,则有 m=n+1
- 具有 n 个节点的完全二叉树的深度为 log2(n+1) 向上取整
- 对于具有 n 个节点的完全二叉树,如果按照从上至下、从左至右的顺序对所有节点从0开始编号,则对序号为 i 的节点有:
* 若 i>0,双亲序号为:(i-1)/2
* 若 i=0,i 为根节点编号,无双亲节点
* 若 2i+1<n,左孩子序号为:2i+1,否则没有左孩子
* 若 2i+2<n,右孩子序号为:2i+2,否则没有右孩子
练习题:
假设一棵完全二叉树中总共有1000个节点,则该二叉树中有____个叶子节点,____个非叶子节点,____个节点只有左孩子,____个节点只有右孩子。
答案:
500、500、1、0
解析:
- 由于这是一个完全二叉树,所以不可能出现只有右孩子的节点,故最后一空为0
- 通过节点个数1000,可以推导出该树的深度为10
- 第10层节点数可以通过总节点数减去前9层节点数得到,为1000-511=489
- 叶子节点数=第10层的节点数+第九层度为0的节点数,而通过第10层的节点数可以知道他们的父节点有489/2+1=245
- 由于这是一个完全二叉树,所以第9层的节点肯定是满的,易得第9层节点数为256,而去除第九层度不为0的节点数,得到第九层叶子节点有256-245=11
- 故叶子节点数为489+11=500,非叶子节点数为1000-500=500
- 而完全二叉树的节点只有左子树的结果有1或0,通过第十层的节点数489为偶数,我们知道肯定有一个父节点只有一个孩子节点,即只有左子树的节点为1
二叉树的存储结构分为:顺序存储(在堆中介绍)和类似于链表的链式存储
二叉树的链式存储是通过一个一个的节点引用起来的,表示方法有:孩子表示法和孩子双亲表示法
孩子表示法:
class Node{
int val; // 数值域
Node left; // 左孩子的引用,常常代表以左孩子为根的整棵树
Node right; // 右孩子的引用,常常代表以右孩子为根的整棵树
}
孩子双亲表示法:
class Node{
int val; // 数值域
Node left; // 左孩子的引用,常常代表以左孩子为根的整棵树
Node right; // 右孩子的引用,常常代表以右孩子为根的整棵树
Node parent; // 当前节点的根节点
}
2.6.1 二叉树的前、中、后序遍历(递归实现)
二叉树是一个非线性的数据结构,对它进行遍历的方式其实有多种,因此如果我们都以自己的方式去遍历二叉树,那么这个代码的易懂性就大大降低,显得很混乱。
为此对于二叉树,根据遍历根节点的先后次序,我们有以下三种遍历方式(N:代表根节点;L:代表根节点的左子树;R:代表根节点的右子树)
- 前序遍历(NLR): 先访问根节点➡根的左子树➡根的右子树
- 中序遍历(LNR): 先访问根的左子树➡根节点➡根的右子树
- 后序遍历(LRN): 先访问根的左子树➡根的右子树➡根节点
练习题:
请写出下面这棵二叉树的四种遍历方式
答案:
- 前序遍历:ABDEHCFG
- 中序遍历:DBEHAFCG
- 后序遍历:DHEBFGCA
注意:
不管是前序、中序还是后序遍历,遍历的路径是一样的,但访问的方式是不一样的
2.6.2 二叉树的层序遍历
除了前中后序遍历外,二叉树还有一种很直观的遍历方式:层序遍历。层序遍历就是从二叉树的根节点出发,首先访问该树的第一层的根节点,然后从左到右访问第二层的节点,接着是第三层的节点,以此类推。
对于上图的树,使用层序遍历,节点被访问的顺序为:ABCDEFGH
层序遍历一般使用非递归的方式,具体的实现方法可以使用队列。
代码: 实现层序遍历
public void levelOrderTraversal(Node root){
if(root==null){
return;
}
Queue queue