二叉树是数据结构中非常重要的非线性结构,其遍历是操作二叉树的基础,指按照一定的顺序访问二叉树中的所有节点,且每个节点仅被访问一次。递归遍历因实现逻辑简洁、符合二叉树的递归定义,成为入门二叉树遍历的首选方式。本文将详细讲解二叉树的前序、中序、后序三种递归遍历的原理、实例演示,并结合代码实现,帮助大家彻底理解这一核心知识点。
一、二叉树的递归定义
二叉树的天然递归性是递归遍历的基础:
- 一棵二叉树要么是空树;
- 要么由根节点 、左子树 和右子树组成,且左子树和右子树也都是二叉树。
基于此,遍历二叉树的核心思路可以拆解为:先处理当前节点 / 子树的根,再递归处理左子树,最后递归处理右子树(前序),或调整三者的顺序,形成中序和后序遍历,这也是递归遍历的代码能高度复用的原因。
二、递归遍历的三种方式
三种遍历的核心区别在于访问根节点的时机 ,左子树始终优先于右子树进行递归处理,这是二叉树递归遍历的通用规则(本文均讨论根左右的遍历体系)。
1. 前序遍历(Pre-order Traversal)
遍历规则
根节点 → 左子树 → 右子树先访问当前根节点,再依次递归遍历左子树和右子树。
核心特点
与二叉树的构建顺序高度契合,常用于二叉树的创建 、复制。
2. 中序遍历(In-order Traversal)
遍历规则
左子树 → 根节点 → 右子树先递归遍历左子树,访问完左子树所有节点后,再访问根节点,最后递归遍历右子树。
核心特点
对于二叉搜索树,中序遍历结果是有序序列,是最常用的遍历方式之一。
3. 后序遍历(Post-order Traversal)
遍历规则
左子树 → 右子树 → 根节点先依次递归遍历左、右子树,待子树全部访问完毕后,最后访问根节点。
核心特点
常用于二叉树销毁 、计算树高 、统计叶子节点。
四、实例验证:完整二叉树的遍历结果
我们以一个结构清晰的二叉树为例,直观展示三种递归遍历的结果:
- 前序遍历(根→左→右) :
A B D H E I C F G J K遍历逻辑:先访问根A,再遍历A的左子树(B为根),最后遍历A的右子树(C为根),子树遵循相同规则。 - 中序遍历(左→根→右) :
H D B E I A F C J G K遍历逻辑:先遍历A的左子树(B为根),再访问根A,最后遍历A的右子树(C为根),子树遵循相同规则。 - 后序遍历(左→右→根) :
H D I E B F J K G C A遍历逻辑:先遍历A的左子树(B为根),再遍历A的右子树(C为根),最后访问根A,子树遵循相同规则。

五、测试结果与遍历过程分析
本文统一测试用例:str[] = "ABDH#K###E##CFI###G#J##"运行代码后得到的遍历结果如下:
- 前序遍历:A B D H K E C F I G J
- 中序遍历:H K D B E A I F C G J
- 后序遍历:K H D E B I F J G C A
核心遍历过程(以根节点 A 为例)
- 前序:先访问 A → 递归遍历 A 的左子树(B 为根) → 递归遍历 A 的右子树(C 为根);
- 中序:先递归遍历 A 的左子树(B 为根) → 访问 A → 递归遍历 A 的右子树(C 为根);
- 后序:先递归遍历 A 的左子树(B 为根) → 递归遍历 A 的右子树(C 为根) → 访问 A。
子树的遍历过程与根节点完全一致,例如左子树根 B 的遍历:仍按照对应规则,先处理 B,再处理 B 的左子树 D,最后处理 B 的右子树 E,以此类推,直到遇到空节点(#)终止递归。
三、代码实现:基于 C 语言的递归遍历
本文所有代码均基于链式存储的二叉树 实现,先通过前序遍历字符串(#表示空节点)构建二叉树,再实现三种递归遍历。(除满二叉树和完全二又树外,其它场景使用数组存储比较浪费空间)

1. 基础结构体与全局定义
cpp
#include <stdio.h>
#include <stdlib.h>
// 定义节点数据类型为字符型
typedef char ElemType;
// 二叉树节点结构体
typedef struct TreeNode
{
ElemType data;
struct TreeNode *lchild; // 左孩子指针
struct TreeNode *rchild; // 右孩子指针
}TreeNode;
// 二叉树类型(节点指针)
typedef TreeNode* BiTree;
// 前序遍历的二叉树构建字符串,#表示空节点
char str[] = "ABDH#K###E##CFI###G#J##";
int idx = 0; // 遍历字符串的索引,全局变量保证递归时持续递增
2. 二叉树的构建(前序递归构建)
cpp
// 构建二叉树,传入二级指针实现对根节点的修改
void createTree(BiTree *T)
{
ElemType ch;
ch = str[idx++]; // 读取当前字符并移动索引
if (ch == '#')
{
*T = NULL; // 空节点,置为NULL
}
else
{
// 分配节点内存
*T = (BiTree)malloc(sizeof(TreeNode));
(*T)->data = ch; // 设置节点数据
createTree(&(*T)->lchild); // 递归构建左子树
createTree(&(*T)->rchild); // 递归构建右子树
}
}
3. 前序递归遍历实现
cpp
// 前序递归遍历
void preOrder(BiTree T)
{
if (T == NULL)
{
return; // 递归终止:空树无需遍历
}
printf("%c ", T->data); // 访问根节点
preOrder(T->lchild); // 递归遍历左子树
preOrder(T->rchild); // 递归遍历右子树
}
4. 中序递归遍历实现
cpp
// 中序递归遍历
void inOrder(BiTree T)
{
if (T == NULL)
{
return;
}
inOrder(T->lchild); // 递归遍历左子树
printf("%c ", T->data); // 访问根节点
inOrder(T->rchild); // 递归遍历右子树
}
5. 后序递归遍历实现
cpp
// 后序递归遍历
void postOrder(BiTree T)
{
if (T == NULL)
{
return;
}
postOrder(T->lchild); // 递归遍历左子树
postOrder(T->rchild); // 递归遍历右子树
printf("%c ", T->data); // 访问根节点
}
6. 主函数测试
cpp
int main(int argc, char const *argv[])
{
BiTree T;
createTree(&T); // 构建二叉树
printf("前序遍历结果:");
preOrder(T);
printf("\n");
printf("中序遍历结果:");
inOrder(T);
printf("\n");
printf("后序遍历结果:");
postOrder(T);
printf("\n");
return 0;
}
7.完整代码如下
cpp
#include <stdio.h>
#include <stdlib.h>
// ====================== 1. 二叉树节点结构体定义 ======================
// 定义二叉树节点存储的数据类型,这里使用字符型
typedef char ElemType;
// 二叉树节点结构体
typedef struct TreeNode
{
ElemType data; // 节点数据域
struct TreeNode *lchild; // 左孩子指针,指向左子树
struct TreeNode *rchild; // 右孩子指针,指向右子树
} TreeNode;
// 给二叉树指针起别名,方便使用
typedef TreeNode* BiTree;
// ====================== 2. 全局变量(构建二叉树使用) ======================
// 前序遍历序列:# 代表空节点,按照这个字符串递归构建二叉树
char str[] = "ABDH#K###E##CFI###G#J##";
int idx = 0; // 字符串遍历索引,全局变量保证递归时索引持续递增
// ====================== 3. 前序递归创建二叉树 ======================
// 功能:根据前序字符串递归创建二叉树
// 参数:二级指针,用于修改传入的根节点指针
void createTree(BiTree *T)
{
// 1. 读取当前字符,索引自增
ElemType ch = str[idx++];
// 2. 递归终止条件:遇到 # 表示空节点
if (ch == '#')
{
*T = NULL;
}
// 3. 非空节点,创建节点并递归构建左右子树
else
{
// 分配节点内存空间
*T = (BiTree)malloc(sizeof(TreeNode));
// 给节点赋值
(*T)->data = ch;
// 递归创建左子树(前序:根 → 左 → 右)
createTree(&(*T)->lchild);
// 递归创建右子树
createTree(&(*T)->rchild);
}
}
// ====================== 4. 前序递归遍历 ======================
// 遍历规则:根节点 → 左子树 → 右子树
void preOrder(BiTree T)
{
// 递归终止条件:节点为空,直接返回
if (T == NULL)
{
return;
}
// 1. 访问根节点(打印数据)
printf("%c ", T->data);
// 2. 递归遍历左子树
preOrder(T->lchild);
// 3. 递归遍历右子树
preOrder(T->rchild);
}
// ====================== 5. 中序递归遍历 ======================
// 遍历规则:左子树 → 根节点 → 右子树
void inOrder(BiTree T)
{
// 递归终止条件
if (T == NULL)
{
return;
}
// 1. 递归遍历左子树
inOrder(T->lchild);
// 2. 访问根节点
printf("%c ", T->data);
// 3. 递归遍历右子树
inOrder(T->rchild);
}
// ====================== 6. 后序递归遍历 ======================
// 遍历规则:左子树 → 右子树 → 根节点
void postOrder(BiTree T)
{
// 递归终止条件
if (T == NULL)
{
return;
}
// 1. 递归遍历左子树
postOrder(T->lchild);
// 2. 递归遍历右子树
postOrder(T->rchild);
// 3. 访问根节点
printf("%c ", T->data);
}
// ====================== 7. 主函数测试 ======================
int main()
{
BiTree T; // 定义二叉树根节点指针
// 1. 递归创建二叉树
createTree(&T);
printf("======== 二叉树递归遍历结果 ========\n");
// 2. 前序遍历
printf("前序遍历:");
preOrder(T);
printf("\n");
// 3. 中序遍历
printf("中序遍历:");
inOrder(T);
printf("\n");
// 4. 后序遍历
printf("后序遍历:");
postOrder(T);
printf("\n");
return 0;
}
8.运行截图

六、递归遍历的核心要点
1. 递归终止条件
节点为 NULL 时直接返回,这是避免递归死循环的关键,也是二叉树递归的天然终止点。
2. 代码的高度复用性
三种遍历代码结构几乎一致,仅根节点访问时机不同,记住左子树永远优先于右子树即可。
3. 递归的底层原理
递归遍历底层依靠系统栈自动保存上下文,无需手动管理入栈、出栈,代码极简。
4. 时间与空间复杂度
- 时间复杂度:O(n),每个节点仅访问一次;
- 空间复杂度:O(h),h 为树的高度。
七、二叉树递归遍历的核心优势
- 逻辑直观,易理解:完全贴合二叉树递归定义,大问题化小,新手极易上手;
- 代码简洁,易编写:三行核心代码实现一种遍历,无需额外数据结构;
- 易扩展,易维护:可快速扩展节点统计、树高计算、路径查找等功能。
八、总结
二叉树递归遍历的核心就是调整根节点的访问时机:
- 前序:根在最前,根→左→右;
- 中序:根在中间,左→根→右;
- 后序:根在最后,左→右→根。
只要理解二叉树的递归结构与终止条件,就能轻松掌握三种遍历。