5.2.1 二叉树
二叉树是一种常见的树状数据结构,它由结点的有限集合组成。一个二叉树要么是空集,被称为空二叉树,要么由一个根结点和两棵不相交的子树组成,分别称为左子树和右子树。每个结点最多有两个子结点,分别称为左子结点和右子结点。
二叉树性质
引理5.1:二叉树中层数为i的结点至多有
个,其中
。
引理5.2:高度为k的二叉树中至多有
个结点,其中
。
引理5.3:设T是由n个结点构成的二叉树,其中叶结点个数为
,度数为2的结点个数为
,则有
。
- 详细证明过程见前文:【数据结构】树与二叉树(三):二叉树的定义、特点、性质及相关证明
满二叉树、完全二叉树定义、特点及相关证明
- 详细证明过程见前文:【数据结构】树与二叉树(四):满二叉树、完全二叉树及其性质
5.2.2 二叉树顺序存储
二叉树的顺序存储是指将二叉树中所有结点按层次顺序存放在一块地址连续的存储空间中,详见: 【数据结构】树与二叉树(五):二叉树的顺序存储(初始化,插入结点,获取父节点、左右子节点等)
5.2.3 二叉树链接存储
二叉树的链接存储系指二叉树诸结点被随机存放在内存空间中,结点之间的关系用指针说明。在链式存储中,每个二叉树结点都包含三个域:数据域(Data)、左指针域(Left)和右指针域(Right),用于存储结点的信息和指向子结点的指针,详见: 【数据结构】树与二叉树(六):二叉树的链式存储
5.2.4 二叉树的遍历
- 遍历(Traversal)是对二叉树中所有节点按照一定顺序进行访问的过程。
- 通过遍历,可以访问树中的每个节点,并按照特定的顺序对它们进行处理。
- 对二叉树的一次完整遍历,可给出树中结点的一种线性排序。
- 在二叉树中,常用的遍历方式有三种:先序遍历、中序遍历和后序遍历。
- 这三种遍历方式都可以递归地进行,它们的区别在于节点的访问顺序。
- 在实现遍历算法时,需要考虑递归终止条件和递归调用的顺序。
- 还可以使用迭代的方式来实现遍历算法,使用栈或队列等数据结构来辅助实现。
- 遍历是二叉树中基础而重要的操作,它为其他许多操作提供了基础,如搜索、插入、删除等。
1-3 先序、中序、后序遍历递归实现及相关练习
【数据结构】树与二叉树(七):二叉树的遍历(先序、中序、后序及其C语言实现)
4. 中序遍历非递归
【数据结构】树与二叉树(八):二叉树的中序遍历(非递归算法NIO)
5. 后序遍历非递归
【数据结构】树与二叉树(九):二叉树的后序遍历(非递归算法NPO)
6. 先序遍历非递归
【数据结构】树与二叉树(十):二叉树的先序遍历(非递归算法NPO)
7. 层次遍历
【数据结构】树与二叉树(十一):二叉树的层次遍历(算法LevelOrder)
5.2.5 二叉树的创建
- 先序遍历
- a b d e f g c
- 中序遍历
- d b f e g a c
- 后序遍历
- d f g e b c a
- 层次遍历
- a b c d e f g
先序创建
由二叉树的遍历,很容易想到用遍历方法去创建二叉树。我们考虑从先根遍历思想出发来构造二叉树。 方法:输入当前被创建结点的数据域的值,如果不空,申请空间用指针指向,然后对数据域进行赋值,再递归对该结点的左右指针域进行赋值,这就是先根创建过程。当输入为空,则算法返回一个空指针(即空树。递归出口)。
a. 算法CBT
b. 代码实现
代码语言:javascript复制struct Node* CBT(char data[], int* index, char tostop) {
char ch = data[(*index) ];
if (ch == tostop) {
return NULL;
} else {
struct Node* t = createNode(ch);
t->left = CBT(data, index, tostop);
t->right = CBT(data, index, tostop);
return t;
}
}
代码整合
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
struct Node {
char data;
struct Node* left;
struct Node* right;
};
struct Node* createNode(char data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("Memory allocation failed!n");
exit(1);
}
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
struct Node* CBT(char data[], int* index, char tostop) {
char ch = data[(*index) ];
if (ch == tostop) {
return NULL;
} else {
struct Node* t = createNode(ch);
t->left = CBT(data, index, tostop);
t->right = CBT(data, index, tostop);
return t;
}
}
void inorderTraversal(struct Node* root) {
if (root != NULL) {
inorderTraversal(root->left);
printf("%c ", root->data);
inorderTraversal(root->right);
}
}
// 创建队列
struct Queue {
struct Node* node;
struct Queue* next;
};
// 初始化队列
void create(struct Queue** front, struct Queue** rear) {
*front = *rear = NULL;
}
// 入队列
void enqueue(struct Queue** front, struct Queue** rear, struct Node* node) {
struct Queue* temp = (struct Queue*)malloc(sizeof(struct Queue));
if (temp == NULL) {
printf("Memory allocation failed!n");
exit(1);
}
temp->node = node;
temp->next = NULL;
if (*rear == NULL) {
*front = *rear = temp;
return;
}
(*rear)->next = temp;
*rear = temp;
}
// 出队列
void dequeue(struct Queue** front) {
if (*front == NULL) {
return;
}
struct Queue* temp = *front;
*front = (*front)->next;
free(temp);
}
// 层次遍历二叉树
void levelOrder(struct Node* root) {
if (root == NULL) {
return;
}
struct Queue* front, * rear;
create(&front, &rear);
enqueue(&front, &rear, root);
while (front != NULL) {
struct Node* current = front->node;
printf("%c ", current->data);
if (current->left != NULL) {
enqueue(&front, &rear, current->left);
}
if (current->right != NULL) {
enqueue(&front, &rear, current->right);
}
dequeue(&front);
}
}
int main() {
char tostop = '#';
char input_data[] = {'a', 'b', 'd', '#', '#', 'e', 'f', '#', '#', 'g', '#', '#', 'c', '#', '#'};
int index = 0;
struct Node* root = CBT(input_data, &index, tostop);
// 层次遍历二叉树
printf("层次遍历二叉树: ");
levelOrder(root);
printf("n");
printf("中序遍历二叉树: ");
inorderTraversal(root);
printf("n");
return 0;
}