二叉树基础与遍历
本节目标
- 理解二叉树节点结构与递归定义
- 掌握前/中/后序(递归 + 迭代)与层序遍历
- 会用遍历解决序列化、路径等问题
节点与四种遍历:
// 运行环境:Node.js 14+
// 保存为 dsa-l11.js,执行:node dsa-l11.js
class TreeNode {
constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right }
}
// 前序(根→左→右)、中序(左→根→右)、后序(左→右→根)
function preorder(root, out = []) { if (!root) return out; out.push(root.val); preorder(root.left, out); preorder(root.right, out); return out }
function inorder(root, out = []) { if (!root) return out; inorder(root.left, out); out.push(root.val); inorder(root.right, out); return out }
function postorder(root, out = []) { if (!root) return out; postorder(root.left, out); postorder(root.right, out); out.push(root.val); return out }
// 层序遍历(BFS,用队列)
function levelOrder(root) {
if (!root) return []
const q = [root], res = []
while (q.length) {
const node = q.shift() // 小数据量无妨;生产用头指针队列
res.push(node.val)
if (node.left) q.push(node.left)
if (node.right) q.push(node.right)
}
return res
}
// 构建示例树: 1
// / \
// 2 3
const root = new TreeNode(1, new TreeNode(2), new TreeNode(3))
console.log('pre ', preorder(root)) // [1, 2, 3]
console.log('in ', inorder(root)) // [2, 1, 3]
console.log('post', postorder(root)) // [2, 3, 1]
console.log('level', levelOrder(root)) // [1, 2, 3]迭代版前序(用栈模拟递归):
function preorderIter(root) {
const res = [], st = []
if (root) st.push(root)
while (st.length) {
const n = st.pop()
res.push(n.val)
if (n.right) st.push(n.right) // 右先入后出,保证左先处理
if (n.left) st.push(n.left)
}
return res
}名词解释
- 二叉树(Binary Tree):每个节点最多有两个子节点(左、右)的树。递归定义:树 = 根节点 + 左子树 + 右子树,所以树算法天然适合递归。
- 遍历(Traversal):按某种顺序访问所有节点。前序"根左右"、中序"左根右"、后序"左右根";层序是逐层从左到右(BFS)。注意中序遍历对 BST 会输出升序。
- DFS vs BFS:深度优先(前中后序,常用栈/递归)一条路走到底再回溯;广度优先(层序,用队列)一层层铺开。
课后练习
- 为什么 BST 的中序遍历是升序的?
- 答案:BST 定义"左子树所有值 < 根 < 右子树所有值",中序是"左→根→右",恰好从小到大访问,故升序。
- 迭代前序为什么"右孩子先入栈"?
- 答案:栈是后进先出,要让左孩子先被弹出处理,就得把右孩子先压栈、左孩子后压栈,弹出时左先出。
总结
二叉树是递归思想最完美的练习场——因为树本身就是"根 + 左子树 + 右子树"的递归定义,所以 90% 的树题都能写成"处理根 + 递归左右"的三行代码。前中后序不是死记硬背的顺序,而是"根在什么时候被访问":根最先是前置、中间是中置、最后是后置,这个视角能让你在写任何树递归时不迷路。层序遍历则切换到 BFS 思维,用队列一层层展开,适合"求最小深度""之字形打印"等问题。我想强调一个工程感悟:递归写起来爽,但太深的树会爆调用栈,此时要学会用显式栈做迭代版(如上面的 preorderIter)。把四种遍历练到闭眼能写,你就拿到了打开整棵"树与堆"章节的钥匙。