概念
除第一个元素没有直接前驱,其他每个元素都有一个直接前驱,每个元素都有一个零个或多个直接后继
- 节点的度、树的度(树内节点的最大值)
- 叶节点、分支节点、内部节点(除了根和叶子节点)
- 关系:孩子、双亲、兄弟、子孙、堂兄弟
- 深度、高度
- 有序树和无序树, 二叉树是有序树
性质
注意:M叉树和度为M的树的区别,M叉树是所有节点的度小于等于m;度为m的树,至少要有一个节点的度为m。
性质主要靠理解
满二叉树(完美二叉树)、完全二叉树
什么样的二叉树是完全二叉树是一个重要的考点 完全二叉树的特点(以下是重要考点)
- 叶节点只可能出现在层次最大的两层
- ,则节点i为分支节点,否则为叶节点,最后一个分支节点的编号为
- 度为1的节点,只可能有一个,该节点只有左孩子,而无右孩子
- n为奇数,每个分支节点都有左右孩子,n为偶数,编号最大的分支节点(n/2)只有左孩子,没有右孩子 以下特点,还可以用于完全二叉树的数组存储
- i>1时,节点i的双亲节点的编号为
- 若节点i有左右孩子,则左孩子的编号为2i,右孩子的编号为2i+1
最重要考点的一个性质 推导过程也要详细记忆
两个特殊需要记忆的
- n0=n2+1
- 知道节点数求高度(老汤的这个推导写得很垃圾,之后有时间写自己的)
链式实现
有各种各样的实现方式,区别在于结构体的定义
- 三叉链表
typedef struct BiTNode {
TElemType data; // 数据域
struct BiTNode *parent; // 指向双亲的指针域
struct BiTNode *left; // 左指针域
struct BiTNode *right; // 右指针域
} BiTNode;
- 二叉链表
typedef struct BiTNode {
TElemType data; // 数据域
struct BiTNode *left; // 左指针域
struct BiTNode *right; // 右指针域
} BiTNode;
顺序结构实现
主要按照完全二叉树的编号来存储,会浪费大量空间
- 顺序存储空间必须是连续的
- 要在顺序存储空间上正确表示二叉树结点之间的逻辑关系
这个结点之间关系的计算写出树来现推,不要记忆,下面的图作为一个参考
遍历二叉树(主要通过链式结构实现)
便利二叉树是按某条搜索路径访问树中每个节点,使得每个节点均被访问一次,而且仅被访问一次,
当拿到一棵树的根节点,就可以顺着左右指针去访问整棵树,所以很多时候给你一棵树,就是给你这棵树的根节点 (这里可以类比线性表的头节点) 所以结构体单独定义一个指针,根节点表示一棵树
先序、中序、后序遍历
先序、中序、后序遍历,这部分掌握得比较牢靠,可以不用管,记住代码是核心
遍历序列确定二叉树
是否由任意两个序列确定一个二叉树?确定的二叉树是否唯一 层序遍历序列和中序遍历序列的结果不是很熟
注意
递归变非递归
树的先序序列和中序序列的关系:以前序序列作为入栈次序,中序序列作为出栈次序
层次遍历
可以比较方便的找到树的高度
根据遍历结构确定二叉树
线索二叉树(Threaded Binary Tree)
快速找到一个节点的前驱和后继
如何快速找到某个节点的先序遍历的前驱和后继?
运行一遍先序遍历算法,时间复杂度O(n)
如何加快查找一个节点在某一种遍历规律中的前驱和后继的速度?
引入线索二叉树:
- 快速找到前驱和后继
- 提高二叉树的存储密度(有些空连域)
- 加速遍历算法
空连接域存放前驱后继的信息,左指向前驱,右指向后继
注意:对于没有空链接域的节点(既有左右孩子的节点)找前驱和后继还是很不方便
一个重要结论:对于一棵具有n个结点的二叉树,它的空链域的数量是8个
结构体:
typedef char TElemType;
Typedef struct BiThrNode{
TElemType data;
struct BiThrNode *left;
struct BiThrNode *right;
int Ltag;//0表示指向左孩子,1指向前驱
int Rtag;//0表示指向有孩子,1指向后继
}BiThrNode,*BiThrTree;
代码有些地方想不通,但是不管了,理解过程,以及遍历
- 最后一个结点的尾指针无论如何都指向头(可能会有一个头节点,如图所示),除了后序线索二叉树
- 减少了出栈入栈的性能损耗,提升先序遍历的性能,下图所示的结点I为例,要递归回上一颗树才能访问结点E,但是利用空链域保存了结点I的后继结点就不用递归,直接访问,同时E也不需要递归返回直接访问C 虽然时间复杂度仍然是O(N),但是空间复杂度降为O(1)
先序二叉树
中序二叉树
后序二叉树