一、二叉树的链式存储
1.1 节点结构
每个节点包含三部分:
-
数据域
-
左孩子指针
-
右孩子指针
c
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode, *Tree;
1.2 创建节点
c
TreeNode* createNode(int value) {
TreeNode *node = (TreeNode*)malloc(sizeof(TreeNode));
if (node == NULL) {
printf("内存分配失败\n");
return NULL;
}
node->data = value;
node->left = NULL;
node->right = NULL;
return node;
}
1.3 构建二叉树
为了方便测试,我们先手动构建一棵二叉树:
text
1
/ \
2 3
/ \ \
4 5 6
c
TreeNode* buildTree() {
TreeNode *root = createNode(1);
root->left = createNode(2);
root->right = createNode(3);
root->left->left = createNode(4);
root->left->right = createNode(5);
root->right->right = createNode(6);
return root;
}
二、二叉树的递归遍历
2.1 前序遍历(根→左→右)
访问顺序:先访问根节点,再遍历左子树,最后遍历右子树。
text
示例树的前序:1 → 2 → 4 → 5 → 3 → 6
c
void preorder(TreeNode *root) {
if (root == NULL) return;
printf("%d ", root->data); // 访问根
preorder(root->left); // 遍历左子树
preorder(root->right); // 遍历右子树
}
递归调用栈(以前序为例):
text
preorder(1)
├─ 打印1
├─ preorder(2)
│ ├─ 打印2
│ ├─ preorder(4)
│ │ ├─ 打印4
│ │ ├─ preorder(NULL)
│ │ └─ preorder(NULL)
│ └─ preorder(5)
│ ├─ 打印5
│ └─ ...
└─ preorder(3)
├─ 打印3
└─ preorder(6)
└─ ...
2.2 中序遍历(左→根→右)
访问顺序:先遍历左子树,再访问根节点,最后遍历右子树。
text
示例树的中序:4 → 2 → 5 → 1 → 3 → 6
c
void inorder(TreeNode *root) {
if (root == NULL) return;
inorder(root->left); // 遍历左子树
printf("%d ", root->data); // 访问根
inorder(root->right); // 遍历右子树
}
2.3 后序遍历(左→右→根)
访问顺序:先遍历左子树,再遍历右子树,最后访问根节点。
text
示例树的后序:4 → 5 → 2 → 6 → 3 → 1
c
void postorder(TreeNode *root) {
if (root == NULL) return;
postorder(root->left); // 遍历左子树
postorder(root->right); // 遍历右子树
printf("%d ", root->data); // 访问根
}
三、层序遍历(广度优先)
3.1 算法思路
层序遍历按树的层次从上到下、从左到右访问节点。需要借助队列:
-
根节点入队
-
当队列不为空时:
-
出队一个节点,访问它
-
如果左孩子存在,左孩子入队
-
如果右孩子存在,右孩子入队
-
text
示例树的层序:1 → 2 → 3 → 4 → 5 → 6
3.2 队列实现
c
// 队列节点
typedef struct QueueNode {
TreeNode *node;
struct QueueNode *next;
} QueueNode;
typedef struct {
QueueNode *front;
QueueNode *rear;
} Queue;
void initQueue(Queue *q) {
q->front = q->rear = NULL;
}
int isEmpty(Queue *q) {
return q->front == NULL;
}
void enqueue(Queue *q, TreeNode *node) {
QueueNode *newNode = (QueueNode*)malloc(sizeof(QueueNode));
newNode->node = node;
newNode->next = NULL;
if (isEmpty(q)) {
q->front = q->rear = newNode;
} else {
q->rear->next = newNode;
q->rear = newNode;
}
}
TreeNode* dequeue(Queue *q) {
if (isEmpty(q)) return NULL;
QueueNode *temp = q->front;
TreeNode *node = temp->node;
q->front = q->front->next;
if (q->front == NULL) q->rear = NULL;
free(temp);
return node;
}
3.3 层序遍历实现
c
void levelOrder(TreeNode *root) {
if (root == NULL) return;
Queue q;
initQueue(&q);
enqueue(&q, root);
while (!isEmpty(&q)) {
TreeNode *cur = dequeue(&q);
printf("%d ", cur->data);
if (cur->left != NULL) {
enqueue(&q, cur->left);
}
if (cur->right != NULL) {
enqueue(&q, cur->right);
}
}
}
四、完整代码演示
c
#include <stdio.h>
#include <stdlib.h>
// 二叉树节点
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
// 队列节点
typedef struct QueueNode {
TreeNode *node;
struct QueueNode *next;
} QueueNode;
typedef struct {
QueueNode *front;
QueueNode *rear;
} Queue;
// 二叉树操作
TreeNode* createNode(int value) {
TreeNode *node = (TreeNode*)malloc(sizeof(TreeNode));
if (node == NULL) return NULL;
node->data = value;
node->left = NULL; node->right = NULL;
return node;
}
TreeNode* buildTree() {
TreeNode *root = createNode(1);
root->left = createNode(2);
root->right = createNode(3);
root->left->left = createNode(4);
root->left->right = createNode(5);
root->right->right = createNode(6);
return root;
}
// 队列操作
void initQueue(Queue *q) {
q->front = q->rear = NULL;
}
int isEmpty(Queue *q) {
return q->front == NULL;
}
void enqueue(Queue *q, TreeNode *node) {
QueueNode *newNode = (QueueNode*)malloc(sizeof(QueueNode));
newNode->node = node;
newNode->next = NULL;
if (isEmpty(q)) {
q->front = q->rear = newNode;
} else {
q->rear->next = newNode;
q->rear = newNode;
}
}
TreeNode* dequeue(Queue *q) {
if (isEmpty(q)) return NULL;
QueueNode *temp = q->front;
TreeNode *node = temp->node;
q->front = q->front->next;
if (q->front == NULL) q->rear = NULL;
free(temp);
return node;
}
// 四种遍历
void preorder(TreeNode *root) {
if (root == NULL) return;
printf("%d ", root->data);
preorder(root->left);
preorder(root->right);
}
void inorder(TreeNode *root) {
if (root == NULL) return;
inorder(root->left);
printf("%d ", root->data);
inorder(root->right);
}
void postorder(TreeNode *root) {
if (root == NULL) return;
postorder(root->left);
postorder(root->right);
printf("%d ", root->data);
}
void levelOrder(TreeNode *root) {
if (root == NULL) return;
Queue q;
initQueue(&q);
enqueue(&q, root);
while (!isEmpty(&q)) {
TreeNode *cur = dequeue(&q);
printf("%d ", cur->data);
if (cur->left != NULL) {
enqueue(&q, cur->left);
enqueue(&q, cur->left);
}
if (cur->right != NULL) {
enqueue(&q, cur->right);
}
}
}
// 释放二叉树
void freeTree(TreeNode *root) {
if (root == NULL) return;
freeTree(root->left);
freeTree(root->right);
free(root);
}
int main() {
TreeNode *root = buildTree();
printf("二叉树结构:\n");
printf(" 1\n");
printf(" / \\\n");
printf(" 2 3\n");
printf(" / \\ \\\n");
printf(" 4 5 6\n\n");
printf("前序遍历(根左右): ");
preorder(root);
printf("\n");
printf("中序遍历(左根右): ");
inorder(root);
printf("\n");
printf("后序遍历(左右根): ");
postorder(root);
printf("\n");
printf("层序遍历: ");
levelOrder(root);
printf("\n");
freeTree(root);
return 0;
}
运行结果:
text
二叉树结构:
1
/ \
2 3
/ \ \
4 5 6
前序遍历(根左右): 1 2 4 5 3 6
中序遍历(左根右): 4 2 5 1 3 6
后序遍历(左右根): 4 5 2 6 3 1
层序遍历: 1 2 3 4 5 6
五、四种遍历的对比总结
| 遍历方式 | 访问顺序 | 示例结果 | 应用场景 |
|---|---|---|---|
| 前序 | 根→左→右 | 1,2,4,5,3,6 | 复制树、求表达式前缀 |
| 中序 | 左→根→右 | 4,2,5,1,3,6 | 二叉搜索树输出有序序列 |
| 后序 | 左→右→根 | 4,5,2,6,3,1 | 删除树、求表达式后缀 |
| 层序 | 按层从左到右 | 1,2,3,4,5,6 | 广度优先搜索、树的可视化 |
六、递归与栈的关系
递归遍历的本质是利用了系统调用栈:
text
前序遍历的递归调用过程:
preorder(1)
├─ 打印1
├─ preorder(2)
│ ├─ 打印2
│ ├─ preorder(4)
│ │ └─ 打印4
│ └─ preorder(5)
│ └─ 打印5
└─ preorder(3)
├─ 打印3
└─ preorder(6)
└─ 打印6
每层递归调用都会把当前状态压栈,返回时弹出。理解这个过程对掌握递归非常重要。
七、复杂度分析
| 遍历方式 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 前序/中序/后序 | O(n) | O(h)(h为树高,最坏O(n)) |
| 层序 | O(n) | O(w)(w为最大宽度,最坏O(n)) |
八、小结
这一篇我们实现了二叉树的链式存储和四种遍历:
| 要点 | 说明 |
|---|---|
| 链式存储 | 节点包含data、left、right |
| 前序遍历 | 根→左→右,递归实现 |
| 中序遍历 | 左→根→右,递归实现 |
| 后序遍历 | 左→右→根,递归实现 |
| 层序遍历 | 借助队列,按层访问 |
递归三要素:
-
终止条件(root == NULL)
-
处理当前层
-
递归调用左右子树
下一篇我们讲由遍历序列重构二叉树。
九、思考题
-
已知前序遍历序列
1 2 4 5 3 6,中序遍历序列4 2 5 1 3 6,如何还原二叉树? -
递归遍历的空间复杂度为什么是O(h)?最坏情况下是多少?
-
如何用栈实现非递归的前序遍历?
-
层序遍历中,如何区分每一层?(即按层输出,而不是一行输出所有)
欢迎在评论区讨论你的答案。