树与二叉树:从概念到遍历的全面解析
在计算机科学中,树(Tree) 是一种极其重要的非线性数据结构,广泛应用于文件系统、数据库索引、编译器语法分析、人工智能决策树等领域。本文将系统地介绍树的基本概念、二叉树的定义与特性,并深入讲解四种核心遍历方式——前序、中序、后序和层序遍历,辅以 JavaScript 代码实现,帮助读者建立清晰而完整的知识体系。
一、什么是树?
自然界中的树有根、枝、叶;数据结构中的树正是对这一结构的抽象:
- 根节点(Root) :整棵树的起点,有且仅有一个。
- 边(Edge) :连接两个节点的线段,表示父子关系。
- 节点(Node) :树的基本单元,包含数据和指向子节点的引用。
- 叶子节点(Leaf) :没有子节点的节点,即“末端”。
树的核心属性
-
层次(Level)
根节点位于第 1 层,其子节点为第 2 层,依此类推。 -
高度(Height)
通常指从根节点到最远叶子节点的最长路径上的边数(也有定义为节点数)。例如,单个节点的树高度为 0 或 1,取决于定义方式。 -
度(Degree)
一个节点的子节点数量称为该节点的度。整棵树的度是所有节点中最大的度。- 叶子节点的度为 0。
- 度为 2 的节点有两个子节点。
⚠️ 注意:树 ≠ 所有节点度都为 2!这是常见误区。
二、二叉树:递归定义的典范
二叉树(Binary Tree) 是树的一种特殊形式,具有严格的结构约束:
定义(递归) :
- 空树是一棵二叉树;
- 非空二叉树由一个根节点、一个左子树和一个右子树组成,且左右子树本身也必须是二叉树。
关键特性
- 左右子树有严格顺序:左 ≠ 右,交换会改变树的结构。
- 每个节点最多有两个子节点(左、右),但可以只有左、只有右,或都没有。
- 不是所有度为 2 的树都是二叉树——二叉树强调“左右”之分,而普通树只关心子节点数量。
节点结构(JavaScript 表示)
class TreeNode {
constructor(val) {
this.val = val;
this.left = null;
this.right = null;
}
}
也可以用对象字面量简洁表达:
const tree = {
val: 1,
left: { val: 2, left: { val: 4 }, right: { val: 5 } },
right: { val: 3 }
};
这种方式直观、可读性强,常用于前端或算法题中的树表示。
三、二叉树的遍历:访问的艺术
遍历(Traversal)是指按某种顺序访问树中所有节点。由于树是非线性的,遍历方式决定了处理逻辑的顺序。
1. 深度优先遍历(DFS)——基于递归
深度优先遍历利用函数调用栈隐式实现,天然契合树的递归结构。
(1)前序遍历(Preorder):根 → 左 → 右
- 应用场景:复制树、打印目录结构(先打印当前目录名)
function preorder(root) {
if (!root) return;
console.log(root.val); // 访问根
preorder(root.left); // 遍历左子树
preorder(root.right); // 遍历右子树
}
(2)中序遍历(Inorder):左 → 根 → 右
- 应用场景:二叉搜索树(BST)的中序遍历结果是升序序列
function inorder(root) {
if (!root) return;
inorder(root.left);
console.log(root.val); // 访问根
inorder(root.right);
}
(3)后序遍历(Postorder):左 → 右 → 根
- 应用场景:删除树(先删子节点再删父节点)、计算目录大小
function postorder(root) {
if (!root) return;
postorder(root.left);
postorder(root.right);
console.log(root.val); // 访问根
}
✅ 记忆口诀:
“前中后”指的是根节点被访问的时机,左右子树顺序始终不变。
2. 广度优先遍历(BFS)——层序遍历(Level Order)
层序遍历按树的层级从上到下、每层从左到右访问节点,需借助队列(Queue) 实现。
实现原理
- 初始将根节点入队;
- 循环出队一个节点,访问它,并将其非空子节点(先左后右)入队;
- 直到队列为空。
function levelorder(root) {
if (!root) return [];
const result = [];
const queue = [root];
while (queue.length) {
const node = queue.shift(); // 出队(FIFO)
result.push(node.val); // 访问
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
return result;
}
- 时间复杂度:O(n),每个节点访问一次。
- 空间复杂度:O(w),w 为树的最大宽度(队列最大长度)。
四、递归与树:天生一对
树的定义本身就是递归的,因此:
-
递归是处理树问题的首选方法;
-
所有深度优先遍历本质上都是递归;
-
递归的关键在于:
- 明确子问题(左子树、右子树);
- 设置终止条件(
if (!root) return)。
五、总结
| 特性 | 说明 |
|---|---|
| 树 | 非线性结构,一个根,多分支,有层次 |
| 二叉树 | 每个节点最多两个子节点,左右顺序不可交换 |
| 前序 | 根→左→右,适合“先处理自身”场景 |
| 中序 | 左→根→右,BST 中得有序序列 |
| 后序 | 左→右→根,适合“先处理子节点”场景 |
| 层序 | 按层遍历,需队列,体现广度优先 |
掌握树与二叉树的核心概念及遍历方法,是理解更高级数据结构(如堆、AVL 树、红黑树、B 树)和算法(如 DFS/BFS、动态规划树形 DP)的基础。无论是面试还是实际开发,这些知识都不可或缺。
🌳 记住:树的本质是递归,遍历的灵魂是顺序。