7、二叉树-四种遍历方式
二叉树的遍历是数据结构中的核心操作之一,主要包括以下四种方式:前序遍历、中序遍历、后序遍历 和 层次遍历。
示例二叉树结构
根节点为 A,A 的左子节点 B,右子节点 C;B 的左子节点 D,右子节点 E;C 的右子节点 F。
结构描述:
A/ \B C/ \ \D E F
🌳 四种常见遍历方式
- 前序遍历(Preorder)
访问顺序:根节点 → 左子树 → 右子树
常用于复制树结构或表达式树的前缀表达式。
void preorder(TreeNode* root) {if (!root) return;printf("%d ", root->val);preorder(root->left);preorder(root->right);
}
- 输出结果
- 访问顺序示例:A → B → D → E → C → F
- 直观场景:把一棵树序列化为“先看父节点再看子节点”的顺序,适合生成前缀表达式或复制树结构(先记录节点,再记录子结构)。
- 中序遍历(Inorder)
访问顺序:左子树 → 根节点 → 右子树
在二叉搜索树中,中序遍历可以得到有序序列。
void inorder(TreeNode* root) {if (!root) return;inorder(root->left);printf("%d ", root->val);inorder(root->right);
}
- 输出结果
- 访问顺序示例:D → B → E → A → C → F
- 直观场景:在二叉搜索树中按中序遍历会得到从小到大的有序序列,适合做排序输出或中序表达式(中缀表达式)。
- 后序遍历(Postorder)
访问顺序:左子树 → 右子树 → 根节点
常用于删除树或表达式树的后缀表达式。
void postorder(TreeNode* root) {if (!root) return;postorder(root->left);postorder(root->right);printf("%d ", root->val);
}
- 输出结果
- 访问顺序示例:D → E → B → F → C → A
- 直观场景:在删除节点或释放资源时先处理子节点再处理父节点;构造后缀表达式或在合并子结果后计算父节点值(先算子,再算父)。
- 层次遍历(Level Order)
按照树的层级逐层访问,通常使用队列实现。
适合用于图形化展示或查找最短路径等问题。
void levelOrder(TreeNode* root) {if (!root) return;Queue q; initQueue(&q);enqueue(&q, root);while (!isEmptyQueue(&q)) {TreeNode* node = dequeue(&q);printf("%d ", node->val);if (node->left) enqueue(&q, node->left);if (node->right) enqueue(&q, node->right);}
}
- 输出结果
- 访问顺序示例:A → B → C → D → E → F
- 直观场景:按层级展示或逐层处理(例如计算树的层数、找最短路径层次、做按层统计),常用于 BFS(广度优先搜索)场景。
递归的本质就是栈
递归依赖于调用栈来保存每次函数调用的状态。每次递归调用都会在栈上压入一个栈帧,栈帧包含参数、局部变量和返回地址。当前调用完成后该栈帧弹出,控制权返回到上一次调用处。
- 对于二叉树遍历,递归过程实际等价于把节点按访问路径压入栈,然后再逐个弹出并处理。
- 递归可以用显式栈结构等价地改写为迭代版本,功能完全相同但控制权由程序员显式管理。
- 优点:代码简洁、逻辑直观。
- 缺点:深度过大可能导致栈溢出。某些语言或编译器对尾递归有优化可以避免额外栈开销,但 C 语言不保证尾递归优化。
c代码实现
#include<stdio.h>
#include <stdlib.h>typedef struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;
}TNode;// 递归前序遍历
void PrevOrder(TNode* root)
{if(root == NULL){printf("NULL ");return;}printf("%d ",root->val);PrevOrder(root->left);PrevOrder(root->right);}// 递归中序遍历
void InOrder(TNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}// 递归后序遍历
void PostOrder(TNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->val);
}// 二叉树大小
int TreeSize(TNode* root)
{if (root == NULL)return 0;return 1 + TreeSize(root->left) + TreeSize(root->right);// 其实所有的数据增加都是靠这个1走到根部之后都是0}// 如果我只想要叶子节点的个数呢
int LeafCount(TNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;return LeafCount(root->left) + LeafCount(root->right);
}int TreeDepth(TNode* root)
{if (root == NULL)return 0;int leftDepth = TreeDepth(root->left);int rightDepth = TreeDepth(root->right);return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1;
}TNode* newNode(int v) {TNode* p = (TNode*)malloc(sizeof(TNode));if (!p) return NULL;p->val = v;p->left = p->right = NULL;return p;
}TNode* buildExampleTree() {TNode* A = newNode(1); // ATNode* B = newNode(2); // BTNode* C = newNode(3); // CTNode* D = newNode(4); // DTNode* E = newNode(5); // ETNode* F = newNode(6); // FA->left = B;A->right = C;B->left = D;B->right = E;C->left = NULL;C->right = F;return A;
}
/*
创建的二叉树的直观结构A/ \B C/ \ \D E F
*/void freeTree(TNode* root) {if (!root) return;freeTree(root->left);freeTree(root->right);free(root);
}int main(){TNode* A = buildExampleTree();PrevOrder(A);printf("\n");InOrder(A);printf("\n");PostOrder(A);printf("\n");int size = TreeSize(A);printf("Tree size = %d\n", size); // 预期输出:Tree size = 6freeTree(A);return 0;}