【数据结构与算法】第20篇:二叉树的链式存储与四种遍历(前序、中序、后序、层序)

一、二叉树的链式存储

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 算法思路

层序遍历按树的层次从上到下、从左到右访问节点。需要借助队列

  1. 根节点入队

  2. 当队列不为空时:

    • 出队一个节点,访问它

    • 如果左孩子存在,左孩子入队

    • 如果右孩子存在,右孩子入队

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
前序遍历 根→左→右,递归实现
中序遍历 左→根→右,递归实现
后序遍历 左→右→根,递归实现
层序遍历 借助队列,按层访问

递归三要素

  1. 终止条件(root == NULL)

  2. 处理当前层

  3. 递归调用左右子树

下一篇我们讲由遍历序列重构二叉树。


九、思考题

  1. 已知前序遍历序列 1 2 4 5 3 6,中序遍历序列 4 2 5 1 3 6,如何还原二叉树?

  2. 递归遍历的空间复杂度为什么是O(h)?最坏情况下是多少?

  3. 如何用栈实现非递归的前序遍历?

  4. 层序遍历中,如何区分每一层?(即按层输出,而不是一行输出所有)

欢迎在评论区讨论你的答案。

相关推荐
小饕8 分钟前
RAG学习之- RAG 数据导入完整指南
人工智能·python·学习
Ulyanov11 分钟前
《玩转QT Designer Studio:从设计到实战》 QT Designer Studio数据绑定与表达式系统深度解析
开发语言·python·qt
王老师青少年编程11 分钟前
csp信奥赛C++高频考点专项训练之贪心算法 --【排序贪心】:魔法
c++·算法·贪心·csp·信奥赛·排序贪心·魔法
wearegogog12312 分钟前
基于和差波束法的单脉冲测角MATLAB实现
人工智能·算法·matlab
AI科技星20 分钟前
灵魂商数(SQ) · 全域数学统一定义【乖乖数学】
算法·机器学习·数学建模·数据挖掘·量子计算
晓觉儿21 分钟前
【GPLT】2026年第十一届团队程序设计天梯赛赛后题解(已写2h,存档中)
数据结构·c++·算法·深度优先·图论
We་ct27 分钟前
LeetCode 322. 零钱兑换:动态规划入门实战
前端·算法·leetcode·typescript·动态规划
cyr___35 分钟前
Unity教程(二十六)技能系统 黑洞技能(上)基础实现
学习·游戏·unity·游戏引擎
星幻元宇VR40 分钟前
VR党建蛋椅|以沉浸式体验推动党建学习方式创新
科技·学习·安全·vr·虚拟现实
棋子入局1 小时前
C语言制作消消乐游戏(4)
c语言·开发语言·游戏