先序遍历是深度优先遍历的一种,口诀是**「根左右」:先访问根,再递归遍历左子树,最后递归遍历右子树。它的关键特征是进入节点即记录**——一个节点刚被到达、还没看任何孩子时,它的值就已经写入输出序列,所以"根永远出现在它的左右子树之前"。
本动画在一棵 7 节点的完全二叉搜索树上完整走一遍(默认 2s/拍,全片 15 拍):
树:根 5(左子 3,右子 8);3 的孩子 1、4;8 的孩子 7、9
先序输出:5 3 1 4 8 7 9
节拍:宣告 1 + 进入即记 7 + 返回父 6 + 完成 1
看点在**"进入即记"**:焦点落到 5 的第一拍输出条就写下 5,随后一头扎进左子树——3、1、4 全部记完才回头碰 8。前半条输出全是左子树,正是"根 → 左 → 右"的字面执行。
返回 p · 查完 n,只移动不记录——这是"左右都看完了才回去"的可视化;✓ 完成 | 先序遍历 7 节点全部输出。| 项目 | 复杂度 | 说明 |
|---|---|---|
| 时间 | O(n) | 每个节点恰好被进入一次、返回一次 |
| 空间 | O(h) | 递归栈深 = 树高 h;平衡树 O(log n),退化成链 O(n) |
| 迭代版 | O(h) | 显式栈模拟:弹栈记根,先压右孩子再压左孩子(左先出) |
#include <iostream>
struct Node {
int val;
Node *left, *right;
Node(int v) : val(v), left(nullptr), right(nullptr) {}
};
void preorder(Node *root) {
if (!root) return;
std::cout << root->val << " "; // 根:进入即记录
preorder(root->left); // 左:整棵左子树
preorder(root->right); // 右:整棵右子树
}
int main() {
// 完全 BST:5(3(1,4), 8(7,9))
Node a(1), b(4), c(3), d(7), e(9), f(8), root(5);
c.left = &a; c.right = &b;
f.left = &d; f.right = &e;
root.left = &c; root.right = &f;
preorder(&root); // 输出 5 3 1 4 8 7 9
return 0;
}
凡是**"先处理父、再处理子"的场合都是先序的主场:树的序列化与重建**(先序 + 空节点标记能唯一还原一棵二叉树)、整棵树的深拷贝(先建父节点才有地方挂孩子)、前缀表达式(波兰式:运算符天然在操作数之前)、目录树的先根序列(先列出目录名再展开内容)。掌握它的诀窍就一句:进入即记——本动画里焦点每落到一个新节点、输出条同拍落字的那一下,就是这句话的样子。