c/c++蓝桥杯经典编程题100道(17)二叉树遍历
二叉树遍历
->返回c/c++蓝桥杯经典编程题100道-目录
目录
二叉树遍历
一、题型解释
二、例题问题描述
三、C语言实现
解法1:递归前序遍历(难度★)
解法2:迭代中序遍历(难度★★)
解法3:层次遍历(BFS,难度★★)
四、C++实现
解法1:递归后序遍历(难度★)
解法2:迭代前序遍历(难度★★)
解法3:锯齿形层次遍历(难度★★★)
五、总结对比表
六、特殊方法与内置函数补充
1. C++ STL容器
2. Morris遍历算法(C语言示例)
一、题型解释
二叉树遍历是按照特定顺序访问树中所有节点的操作。常见题型:
-
基础遍历:前序、中序、后序遍历(递归或迭代实现)。
-
层次遍历(BFS):按层从上到下访问节点。
-
变种遍历:
-
锯齿形层次遍历(Zigzag遍历)。
-
垂序遍历(按列访问节点)。
-
-
Morris遍历:使用线索二叉树实现O(1)空间复杂度的遍历。
二、例题问题描述
例题1:输入二叉树:
1
/ \
2 3
/ \
4 5
前序遍历输出 1 2 4 5 3
,中序遍历输出 4 2 5 1 3
,后序遍历输出 4 5 2 3 1
。
例题2:输入同上二叉树,层次遍历输出 1 2 3 4 5
。
例题3:锯齿形层次遍历输出 1 3 2 4 5
(第二层反向)。
三、C语言实现
解法1:递归前序遍历(难度★)
通俗解释:
-
像“根→左→右”的顺序探访每个房间,先访问根节点,再递归访问左右子树。
c
#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
void preorderTraversal(TreeNode *root) {
if (root == NULL) return;
printf("%d ", root->val); // 先访问根节点
preorderTraversal(root->left); // 递归左子树
preorderTraversal(root->right); // 递归右子树
}
int main() {
// 构建例题1的二叉树
TreeNode n4 = {4, NULL, NULL};
TreeNode n5 = {5, NULL, NULL};
TreeNode n2 = {2, &n4, &n5};
TreeNode n3 = {3, NULL, NULL};
TreeNode n1 = {1, &n2, &n3};
printf("前序遍历:");
preorderTraversal(&n1); // 输出 1 2 4 5 3
return 0;
}
代码逻辑:
-
递归终止条件:当前节点为NULL时返回。
-
访问顺序:根节点→左子树→右子树。
-
时间复杂度:O(n),每个节点访问一次。
解法2:迭代中序遍历(难度★★)
通俗解释:
-
用栈模拟递归过程,像“先深入左子树到底,再回溯访问根节点,最后处理右子树”。
c
#include <stdbool.h>
#define MAX_SIZE 100
typedef struct Stack {
TreeNode* data[MAX_SIZE];
int top;
} Stack;
void push(Stack *s, TreeNode *node) {
s->data[++s->top] = node;
}
TreeNode* pop(Stack *s) {
return s->data[s->top--];
}
bool isEmpty(Stack *s) {
return s->top == -1;
}
void inorderTraversal(TreeNode *root) {
Stack s = { .top = -1 };
TreeNode *curr = root;
while (curr != NULL || !isEmpty(&s)) {
while (curr != NULL) { // 深入左子树
push(&s, curr);
curr = curr->left;
}
curr = pop(&s); // 回溯到父节点
printf("%d ", curr->val);
curr = curr->right; // 处理右子树
}
}
int main() {
printf("\n中序遍历:");
inorderTraversal(&n1); // 输出 4 2 5 1 3
return 0;
}
代码逻辑:
-
栈辅助:用栈保存未处理的节点。
-
左链入栈:不断将左子节点压栈,直到最左端。
-
回溯访问:弹出栈顶节点访问,转向右子树。
解法3:层次遍历(BFS,难度★★)
通俗解释:
-
像逐层扫描,用队列记录每层节点,先访问上层再下层。
c
#include <stdbool.h>
#define MAX_SIZE 100
typedef struct Queue {
TreeNode* data[MAX_SIZE];
int front, rear;
} Queue;
void enqueue(Queue *q, TreeNode *node) {
q->data[q->rear++] = node;
}
TreeNode* dequeue(Queue *q) {
return q->data[q->front++];
}
bool isEmpty(Queue *q) {
return q->front == q->rear;
}
void levelOrderTraversal(TreeNode *root) {
if (root == NULL) return;
Queue q = { .front = 0, .rear = 0 };
enqueue(&q, root);
while (!isEmpty(&q)) {
TreeNode *node = dequeue(&q);
printf("%d ", node->val);
if (node->left) enqueue(&q, node->left);
if (node->right) enqueue(&q, node->right);
}
}
int main() {
printf("\n层次遍历:");
levelOrderTraversal(&n1); // 输出 1 2 3 4 5
return 0;
}
代码逻辑:
-
队列初始化:根节点入队。
-
循环处理:出队节点并访问,将其子节点入队。
-
逐层遍历:队列先进先出特性保证按层访问。
四、C++实现
解法1:递归后序遍历(难度★)
通俗解释:
-
按“左→右→根”顺序访问,先处理左右子树,最后访问根节点。
cpp
#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
void postorderTraversal(TreeNode *root) {
if (!root) return;
postorderTraversal(root->left); // 先左子树
postorderTraversal(root->right); // 再右子树
cout << root->val << " "; // 最后根节点
}
int main() {
TreeNode *root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
cout << "后序遍历:";
postorderTraversal(root); // 输出 4 5 2 3 1
return 0;
}
代码逻辑:
-
与C语言递归类似,但使用C++的类和指针语法。
解法2:迭代前序遍历(难度★★)
通俗解释:
-
用栈模拟递归,显式控制访问顺序(根→左→右)。
cpp
#include <stack>
void preorderIterative(TreeNode *root) {
if (!root) return;
stack<TreeNode*> s;
s.push(root);
while (!s.empty()) {
TreeNode *node = s.top();
s.pop();
cout << node->val << " ";
if (node->right) s.push(node->right); // 右子先入栈(后处理)
if (node->left) s.push(node->left); // 左子后入栈(先处理)
}
}
int main() {
cout << "\n迭代前序遍历:";
preorderIterative(root); // 输出 1 2 4 5 3
return 0;
}
代码逻辑:
-
根节点入栈:栈顶为当前访问的根节点。
-
右子先入栈:利用栈的LIFO特性,确保左子先被处理。
解法3:锯齿形层次遍历(难度★★★)
通俗解释:
-
层次遍历的变种,偶数层反向输出(用双端队列控制方向)。
cpp
#include <queue>
#include <vector>
void zigzagLevelOrder(TreeNode *root) {
if (!root) return;
queue<TreeNode*> q;
q.push(root);
bool leftToRight = true;
while (!q.empty()) {
int size = q.size();
vector<int> level(size);
for (int i = 0; i < size; i++) {
TreeNode *node = q.front();
q.pop();
int index = leftToRight ? i : size - 1 - i;
level[index] = node->val;
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
leftToRight = !leftToRight;
for (int num : level) cout << num << " ";
}
}
int main() {
cout << "\n锯齿形遍历:";
zigzagLevelOrder(root); // 输出 1 3 2 4 5
return 0;
}
代码逻辑:
-
队列记录当前层:每次处理一层节点。
-
方向控制:根据层数奇偶性决定填充顺序。
五、总结对比表
方法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
---|---|---|---|---|
递归遍历 | O(n) | O(n) | 代码简洁 | 栈溢出风险 |
迭代遍历 | O(n) | O(n) | 无栈溢出风险 | 需手动管理栈/队列 |
层次遍历(BFS) | O(n) | O(n) | 直观,适合按层处理 | 空间占用较大 |
Morris遍历 | O(n) | O(1) | 无需额外空间 | 修改树结构,逻辑复杂 |
六、特殊方法与内置函数补充
1. C++ STL容器
-
stack<TreeNode*>
:用于迭代遍历,支持push()
,pop()
,top()
。 -
queue<TreeNode*>
:用于层次遍历,支持push()
,front()
,pop()
。
2. Morris遍历算法(C语言示例)
通俗解释:
-
通过修改树的指针,将空间复杂度降为O(1)。
c
void morrisInorder(TreeNode *root) {
TreeNode *curr = root, *pre;
while (curr != NULL) {
if (curr->left == NULL) { // 无左子树,直接访问
printf("%d ", curr->val);
curr = curr->right;
} else {
pre = curr->left;
while (pre->right != NULL && pre->right != curr) {
pre = pre->right; // 找到左子树的最右节点
}
if (pre->right == NULL) { // 建立线索
pre->right = curr;
curr = curr->left;
} else { // 删除线索并访问
pre->right = NULL;
printf("%d ", curr->val);
curr = curr->right;
}
}
}
}
代码逻辑:
-
线索化:将当前节点的前驱节点的右指针指向自己,实现回溯。
-
遍历与恢复:访问后删除线索,恢复树结构。
c/c++蓝桥杯经典编程题100道-目录-CSDN博客