二叉树的前、中、后序递归遍历(完整解析与实现)

107 阅读7分钟

二叉树的前、中、后序递归遍历(完整解析与实现)

编写的这份二叉树代码完美实现了二叉树的创建前、中、后序递归遍历,符合数据结构中二叉树的基础实现规范。下面结合数据结构专栏的知识点,从代码核心解析、遍历原理、运行结果、扩展知识点四个方面进行全面讲解。

一、代码核心结构解析(数据结构基础)

1. 二叉树节点结构体定义

c

运行

typedef struct Node
{
	int data;               // 节点数据域,存储节点值
	struct Node *left;      // 左孩子节点指针,指向左子树
	struct Node *right;     // 右孩子节点指针,指向右子树
}Node;
  • 数据结构知识点:二叉树的每个节点包含数据域两个指针域,分别指向左子节点和右子节点,这是二叉树的基本节点结构;
  • 设计亮点:使用typedef简化结构体定义,后续可直接用Node代替struct Node,简化代码书写;
  • 注意点:指针域初始化为NULL(表示无左 / 右子树),避免野指针问题。

2. 节点创建函数(create

c

运行

Node *create(int data)
{
	// 1. 为节点分配内存空间,大小为Node结构体所占字节数
	Node *node=(Node*)malloc(sizeof(Node));
	// 2. 内存分配失败判断(健壮性设计)
	if(node==NULL)
	{
		printf("创建失败!\n");
		exit(0);
	}
	// 3. 初始化节点数据域和指针域
	node->data=data;
	node->left=node->right=NULL; // 初始无左、右子树
	return node;
}
  • 核心功能:创建一个独立的二叉树节点,完成内存分配与初始化;
  • 数据结构知识点:二叉树节点的动态创建依赖malloc函数分配堆内存,避免栈内存随函数调用结束而释放;
  • 健壮性设计:判断malloc返回值是否为NULL,处理内存分配失败的异常场景,避免程序崩溃;
  • 初始化规范:新节点的左、右指针均设为NULL,表示该节点初始为叶子节点(无后代节点)。

3. 二叉树构建(main函数中)

c

运行

Node *root =create(1);
root->left =create(2);
root->right =create(3);
root->left->left =create(4);
root->left->right =create(5);
  • 构建的二叉树结构(与注释一致,符合完全二叉树的部分结构):

plaintext

        1(根节点)
      /   \
     2     3(右子树根节点)
    / \
   4   5(叶子节点)
  • 构建逻辑:从根节点开始,逐层创建子节点,通过指针赋值建立节点间的父子关系;
  • 注意点:构建顺序为先根节点,再左子树,后右子树,符合二叉树的层次结构特点。

二、二叉树三种递归遍历原理(核心知识点)

二叉树的前、中、后序遍历属于深度优先遍历(DFS) ,核心区别在于访问根节点的时机不同,而左子树始终优先于右子树遍历(这是遍历的默认规则)。

1. 前序遍历(Preorder):根 → 左 → 右

c

运行

void Preorder(Node *root)
{
	if(root!=NULL) // 递归终止条件:节点为NULL(无节点可访问)
	{
		printf("%d ",root->data);  // 第一步:访问根节点(输出数据)
		Preorder(root->left);      // 第二步:递归遍历左子树
		Preorder(root->right);     // 第三步:递归遍历右子树
	}
}
  • 遍历规则:先访问当前节点(根),再递归遍历左子树,最后递归遍历右子树;
  • 递归终止条件:root!=NULL,当节点为NULL时,说明当前子树无节点,直接返回,结束当前递归分支;
  • 遍历结果(对应示例二叉树):1 2 4 5 3

2. 中序遍历(Inorder):左 → 根 → 右

c

运行

void Inorder(Node *root)
{
	if(root!=NULL)
	{
		Inorder(root->left);      // 第一步:递归遍历左子树
		printf("%d ",root->data);  // 第二步:访问根节点(输出数据)
		Inorder(root->right);     // 第三步:递归遍历右子树
	}
}
  • 遍历规则:先递归遍历左子树,再访问当前节点(根),最后递归遍历右子树;
  • 核心特点:对于二叉搜索树(BST),中序遍历结果为升序排列(这是二叉搜索树的重要特性);
  • 遍历结果(对应示例二叉树):4 2 5 1 3

3. 后序遍历(Postorder):左 → 右 → 根

c

运行

void Postorder(Node *root)
{
	if(root!=NULL)
	{
		Postorder(root->left);      // 第一步:递归遍历左子树
		Postorder(root->right);     // 第二步:递归遍历右子树
		printf("%d ",root->data);   // 第三步:访问根节点(输出数据)
	}
}
  • 遍历规则:先递归遍历左子树,再递归遍历右子树,最后访问当前节点(根);
  • 核心特点:后序遍历中,根节点最后被访问,常用于二叉树的销毁(先销毁子树,再销毁根节点)、计算子树权重等场景;
  • 遍历结果(对应示例二叉树):4 5 2 3 1

三、运行结果与优化(提升代码可读性)

1. 修正main函数输出格式(优化后)

原代码输出缺少遍历名称说明,优化后更清晰:

c

运行

int main()
{
	Node *root =create(1);
	root->left =create(2);
	root->right =create(3);
	root->left->left =create(4);
	root->left->right =create(5);
	
	printf("前序遍历结果:");
	Preorder(root);
	printf("\n");
	
	printf("中序遍历结果:");
	Inorder(root);
	printf("\n");
	
	printf("后序遍历结果:");
	Postorder(root);
	printf("\n");
	
	// 补充:释放二叉树内存(避免内存泄漏,后续知识点)
	// FreeTree(root);
	return 0;
}

2. 运行结果(控制台输出)

plaintext

前序遍历结果:1 2 4 5 3 
中序遍历结果:4 2 5 1 3 
后序遍历结果:4 5 2 3 1 

四、数据结构专栏扩展知识点(深化理解)

1. 递归遍历的本质:函数调用栈

二叉树的递归遍历依赖系统函数调用栈,每一次递归调用都会将当前节点的上下文压入栈中,当递归终止(节点为NULL)时,栈顶元素出栈,继续执行后续逻辑。

  • 例如前序遍历节点1时,会先压入1,再递归压入244的左、右子树为NULL,弹出4并输出,再处理4的父节点2的右子树5,以此类推。

2. 非递归遍历(补充知识点)

递归遍历简洁但存在栈溢出风险(二叉树深度过大时),实际开发中常使用 ** 栈(手动实现)** 完成非递归遍历,核心思路是手动模拟函数调用栈的过程。

  • 示例:前序遍历的非递归实现(使用数组模拟栈):

c

运行

void PreorderNonRecursive(Node *root)
{
	if(root == NULL) return;
	Node *stack[100]; // 手动创建栈,存储节点指针
	int top = -1;     // 栈顶指针,初始为-1(空栈)
	stack[++top] = root; // 根节点入栈
	
	while(top >= 0) // 栈不为空时循环
	{
		Node *cur = stack[top--]; // 栈顶元素出栈
		printf("%d ", cur->data); // 访问当前节点
		
		// 右子树先入栈,左子树后入栈(栈先进后出,保证左子树先访问)
		if(cur->right != NULL) stack[++top] = cur->right;
		if(cur->left != NULL) stack[++top] = cur->left;
	}
}

3. 二叉树内存释放(避免内存泄漏)

动态创建的二叉树节点占用堆内存,程序结束前需手动释放,推荐使用后序遍历(先释放子树,再释放根节点):

c

运行

void FreeTree(Node *root)
{
	if(root != NULL)
	{
		FreeTree(root->left);  // 释放左子树
		FreeTree(root->right); // 释放右子树
		free(root);            // 释放当前节点
		root = NULL;           // 避免野指针
	}
}

五、总结

  1. 二叉树的三种递归遍历核心区别在于根节点的访问时机,左子树始终优先于右子树遍历;
  2. 递归遍历的关键是递归终止条件(root!=NULL遍历顺序,代码简洁且易理解;
  3. 这份代码是二叉树遍历的入门基础,掌握后可进一步学习二叉搜索树、平衡二叉树(AVL)等高级二叉树结构;
  4. 实际开发中,需注意内存泄漏问题,使用完毕后要手动释放二叉树的堆内存。

这份代码完美契合数据结构专栏的基础知识点,是理解二叉树遍历的绝佳案例,吃透递归遍历的原理后,可顺利过渡到非递归遍历和高级二叉树的学习。