1.二叉树
1.1 二叉树的定义
首先先来回顾一下什么是二叉树:
二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。
二叉树的五种可能的形态:
1.2 二叉树相关术语
- 根节点(root node):位于二叉树顶层的节点,没有父节点。
- 叶节点(leaf node):没有子节点的节点,其两个指针均指向None 。
- 边(edge):连接两个节点的线段,即节点引用(指针)。
- 节点所在的层(level):从顶至底递增,根节点所在层为 1 。
- 节点的度(degree):节点的子节点的数量。在二叉树中,度的取值范围是 0、1、2 。
- 二叉树的高度(height):从根节点到最远叶节点所经过的边的数量。
- 节点的深度(depth):从根节点到该节点所经过的边的数量。
- 节点的高度(height):从距离该节点最远的叶节点到该节点所经过的边的数量。
1.3 二叉树的性质
性质1:二叉树的第 i 层上至多有 个结点()
性质2:深度为 k 二叉树至多有 个结点()
性质3:对任意一棵二叉树T,如果其叶子数为 ,度为2的结点数为, 则
性质4:具有 n () 个结点的完全二叉树的深度为 或
性质5:如果对一棵具有 n 个结点的完全二叉树的结点进行编号,则对任一结
(1) 若 ,则该结点是二叉树的根,无双亲; 否则,其双亲结点的编号为 或 ;
(2)如果 则结点 没有左孩子, 否则,其左孩子的编号为 ;
(3)如果 则结点 没有右孩子,否则,其右孩子的编号为 ;
(4)若 为奇数, 且 ,则其左兄弟为 ,
若 为偶数, 且 , 则其右兄弟为 。
2.二叉树的遍历
从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成。因此,在任一给定结点上,可以按某种次序执行三个操作:
① 访问结点本身(N)
② 遍历该结点的左子树(L)
③ 遍历该结点的右子树(R)
在二叉树中,常见的遍历有先序遍历、中序遍历、后序遍历以及层序遍历,他们的主要思想分别是:
前序遍历:根结点 --> 左子树 --> 右子树
// 先序遍历
void PreOrderTraverse(BiTree T) {
if (T == NULL) {
return;
}
cout << T->data;
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
中序遍历:左子树 --> 根结点 --> 右子树
// 中序遍历
void InOrderTraverse(BiTree T) {
if (T == NULL) {
return;
}
InOrderTraverse(T->lchild);
cout << T->data;
InOrderTraverse(T->rchild);
}
后序遍历:左子树 --> 右子树 --> 根结点
// 后序遍历
void PostOrderTraverse(BiTree T) {
if (T == NULL) {
return;
}
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
cout << T->data;
}
层次遍历:自上而下,自左至右逐层访问树的结点
// 层序遍历
void LevelOrderTraverse(BiTree T) {
BiTree queue[100]; // 队列
int front = 0, rear = 0;
if (T != NULL) {
rear = (rear + 1) % 100;
queue[rear] = T;
}
while (front != rear) {
int levelSize = (rear - front + 100) % 100; // 当前层的结点个数
for(int i = 0; i < levelSize; ++i){
front = (front + 1) % 100;
BiTree p = queue[front];
cout << p->data;
// 将当前结点的左右孩子入队
if (p->lchild != NULL) {
rear = (rear + 1) % 100;
queue[rear] = p->lchild;
}
if (p->rchild != NULL) {
rear = (rear + 1) % 100;
queue[rear] = p->rchild;
}
}
cout << endl;
}
}
层序遍历的算法思想:
①初始化一个辅助队列
②根节点入队
③若队列非空,则队头结点出队,访问该结点,并将其左、右孩子插入队尾(如果有的话)
// 层序遍历
void LevelOrderTraverse(BiTree T) {
BiTree queue[100]; // 队列
int front = 0, rear = 0;
if (T != NULL) {
rear = (rear + 1) % 100;
queue[rear] = T;
}
while (front != rear) {
int levelSize = (rear - front + 100) % 100; // 当前层的结点个数
for(int i = 0; i < levelSize; ++i){
front = (front + 1) % 100;
BiTree p = queue[front];
cout << p->data;
// 将当前结点的左右孩子入队
if (p->lchild != NULL) {
rear = (rear + 1) % 100;
queue[rear] = p->lchild;
}
if (p->rchild != NULL) {
rear = (rear + 1) % 100;
queue[rear] = p->rchild;
}
}
cout << endl;
}
}