说实话,大多数人第一次学二叉树,都是一脸懵的。
一边是「每个节点最多两个子节点」
一边是「前序、中序、后序、递归、回溯」
明明规则不复杂,脑子却完全跟不上。
其实问题不在二叉树,而在没人把它讲成人话。
这篇文章,我想换一种方式:
站在“正在学的人”这一边,把二叉树从 0 捋顺。
一、先别急着记定义,二叉树到底在解决什么问题?
我们先抛开术语。
想象一个最常见的需求:
我要存一堆数据,还希望它们:
- 有层级关系
- 查找不要太慢
- 能用递归优雅地处理
这时候,树结构就出现了。
而二叉树,是所有树结构里最简单、也最重要的一种。
规则只有一条:
每个节点,最多两个孩子
一个叫左,一个叫右
就这么简单。
二、二叉树的几种常见“形态”
1️⃣ 满二叉树:最理想的情况
满二叉树你可以理解成:
每一层都挤满了节点
1
/ \
2 3
/ \ / \
4 5 6 7
它的意义不在于你会不会用,而在于:
- 很多数学公式都是从它推出来的
- 是其他二叉树的“参照物”
2️⃣ 完全二叉树:工程里最常见的形态
完全二叉树的关键词只有四个字:
从左到右
只要你记住这句话,基本不会错。
1
/ \
2 3
/ \ /
4 5 6
为什么它重要?
因为它可以直接用数组存。
也正是因为这一点:
- 堆
- 优先队列
底层都是完全二叉树。
3️⃣ 二叉搜索树:第一次让你觉得“树有点用了”
二叉搜索树(BST)有一个非常“反直觉但好用”的规则:
左边都比我小,右边都比我大
5
/ \
3 8
一旦你接受了这个规则,就会发现:
- 查找可以很快
- 中序遍历直接变成排序
这是面试最爱考的一种树
4️⃣ 平衡二叉搜索树:专门治“退化”的
普通 BST 有个致命问题:
如果你按顺序插数据,它会直接变成链表
平衡二叉搜索树的存在,就是为了干一件事:
强行让树别歪
只要记住一句话就够了:
任意节点左右子树高度差 ≤ 1
三、JS 里,我们一般怎么“表示”一棵二叉树?
在前端世界里,99% 用的都是链式存储。
原因很简单:
- 直观
- 好递归
- 好调试
class TreeNode {
constructor(value) {
this.value = value
this.left = null
this.right = null
}
}
看到这个结构,你可以把它当成:
一个“自己指向左右孩子的对象”
四、遍历,其实就是“你打算先看谁?”
遍历这个词听着很唬人,其实就一个问题:
你打算什么时候访问当前节点?
前序遍历:我先来
function preorder(root) {
if (!root) return
console.log(root.value)
preorder(root.left)
preorder(root.right)
}
特点:
- 根节点最先访问
- 很适合“复制一棵树”
中序遍历:左边先说话
function inorder(root) {
if (!root) return
inorder(root.left)
console.log(root.value)
inorder(root.right)
}
在 BST 中:
你会直接得到一个升序数组
后序遍历:最后才轮到我
function postorder(root) {
if (!root) return
postorder(root.left)
postorder(root.right)
console.log(root.value)
}
常见用途:
- 删除节点
- 计算高度
五、层序遍历:一层一层来,别急
这个就是 BFS,本质是:
排队
function levelOrder(root) {
if (!root) return
const queue = [root]
while (queue.length) {
const node = queue.shift()
console.log(node.value)
if (node.left) queue.push(node.left)
if (node.right) queue.push(node.right)
}
}
六、你真正要记住的,其实只有这三件事
1️⃣ 二叉树是递归思维的最佳训练场
2️⃣ 中序遍历 = BST 排序
3️⃣ BFS 一定用队列,DFS 一定靠递归
结语
二叉树不是为了难你。
它只是在逼你:
用更结构化的方式思考问题
一旦你真正理解了它:
- 递归会突然变简单
- 算法题会开始有“套路感”
- 很多框架底层设计也能看懂了