二叉树刷题通关秘籍:从翻转到众数,LeetCode 常见套路一网打尽!

6 阅读4分钟

“树”立信心,“码”到成功!
—— 本文带你用轻松幽默的方式掌握 LeetCode 二叉树高频题型,附带 JS 实现,助你面试不再怕树!


🧠 为什么 LeetCode 总爱考「二叉树」?

因为——它结构清晰、递归友好、套路明显,简直是算法题界的“模范生”!

而且,只要你掌握了那几招核心思想(前中后序遍历、DFS/BFS、分治、BST 性质),80% 的二叉树题都能迎刃而解。

今天,我们就以 28 道经典 LeetCode 二叉树题目为线索,一起打通任督二脉,顺便笑出腹肌 😎


🌱 第一式:遍历三剑客 —— 前序、中序、后序

口诀:前中后 = 根左右 / 左根右 / 左右根

这三位是所有二叉树操作的基石。记住:递归写法简洁如诗,迭代写法锻炼栈功底

// 前序(根左右)
var preorderTraversal = function(root) {
  let res = [];
  const dfs = (node) => {
    if (!node) return;
    res.push(node.val);
    dfs(node.left);
    dfs(node.right);
  };
  dfs(root);
  return res;
}

小贴士:前序适合“复制树”或“路径记录”;中序在 BST 中能输出有序数组;后序常用于“删除”或“计算子树和”。


🔁 第二式:层序遍历 —— BFS 的主场

队列出场,逐层收割!

var levelOrder = function(root) {
  if (!root) return [];
  let queue = [root], res = [];
  while (queue.length) {
    let len = queue.length, level = [];
    for (let i = 0; i < len; i++) {
      let node = queue.shift();
      level.push(node.val);
      node.left && queue.push(node.left);
      node.right && queue.push(node.right);
    }
    res.push(level);
  }
  return res;
}

💡 应用场景:找树的左下角值(513)、最大宽度、按层打印……甚至可以用来判断完全二叉树!


🔄 第三式:翻转 & 合并 —— 递归的魔法

翻转二叉树(226)—— Google 面试题!

var invertTree = function(root) {
  if (!root) return null;
  [root.left, root.right] = [invertTree(root.right), invertTree(root.left)];
  return root;
}

🤯 梗来了:有人问 Max Howell(Homebrew 作者):“你能写出翻转二叉树吗?”
他答:“我写了 Homebrew,但不会翻转二叉树。”
结果被 Google 拒了……所以,这题你必须会!

合并二叉树(617)

两棵树叠在一起?重叠就相加,不重叠就继承!

var mergeTrees = function(r1, r2) {
  if (!r1) return r2;
  if (!r2) return r1;
  r1.val += r2.val;
  r1.left = mergeTrees(r1.left, r2.left);
  r1.right = mergeTrees(r1.right, r2.right);
  return r1;
}

❤️ 浪漫解读:这不是合并树,这是“爱情树”——你中有我,我中有你!


🌲 第四式:二叉搜索树(BST)专属技能包

BST 是有纪律的树:左 < 根 < 右。利用这个性质,很多操作可以 O(log n) 完成!

1. 搜索(700)—— 直接二分!

if (root.val === val) return root;
return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val);

2. 插入(701)—— 找到空位就坐下!

if (!root) return new TreeNode(val);
if (val < root.val) root.left = insertIntoBST(root.left, val);
else root.right = insertIntoBST(root.right, val);
return root;

3. 删除(450)—— 最难搞的“善后工作”

  • 没孩子?直接删。
  • 一个孩子?孩子顶上。
  • 两个孩子?找右子树最小值(或左子树最大值)来接班!
// 两个孩子的情况
let cur = root.right;
while (cur.left) cur = cur.left; // 找右子树的最左节点
cur.left = root.left;
return root.right;

⚠️ 注意:别忘了递归更新父节点的指针!

4. 验证 BST(98)—— 中序遍历看是否升序!

buildArr(root); // 中序收集值
for (let i = 1; i < arr.length; i++) {
  if (arr[i] <= arr[i-1]) return false;
}

❗ 别踩坑:不能只比较 root 和左右孩子!要全局有序!


🎯 第五式:路径 & 深度 & 平衡 —— DFS 的高光时刻

路径总和(112)

从根到叶子,是否存在一条路加起来等于 target?

traversal(root, targetSum - root.val);
// 递归时传递剩余值,到叶子时 check 是否为 0

最大/最小深度(104 / 111)

  • 最大深度:1 + max(left, right)
  • 最小深度:注意单边为空不算叶子!
if (!root.left) return 1 + minDepth(root.right);
if (!root.right) return 1 + minDepth(root.left);

平衡二叉树(110)

每个子树的左右高度差 ≤ 1。用 -1 表示不平衡,提前剪枝!

if (Math.abs(left - right) > 1) return -1;

🧩 第六式:高级玩法 —— 构造 & 修剪 & 累加

从中序+后序建树(106)

  • 后序最后一个 = 根
  • 在中序中找根,分割左右子树
  • 递归构建

🧠 关键:数组切片要精准,别把边界搞错!

修剪 BST(669)

值太小?砍掉左边,去右边找;
值太大?砍掉右边,去左边找;
否则,左右都修!

if (root.val < low) return trimBST(root.right, low, high);
if (root.val > high) return trimBST(root.left, low, high);

累加树(538)—— 反向中序!

从大到小遍历,累加前面的值:

ReverseInOrder(root.right);
cur.val += pre;
pre = cur.val;
ReverseInOrder(root.left);

💡 这叫“右 → 根 → 左”,是中序的镜像!


📊 第七式:统计类问题 —— 中序是你的朋友

最小绝对差(530)

BST 中任意两数最小差?中序遍历后相邻元素差最小!

众数(501)

同样用中序,边遍历边统计频率,动态更新答案数组。

技巧:不用哈希表!利用 BST 中序有序,相同值会连续出现。


🏁 总结:二叉树刷题心法

类型核心思路典型题目
遍历递归三部曲94, 144, 145, 102
构造分治 + 递归106, 108, 654
BST 操作利用有序性700, 701, 450, 98
路径/深度DFS 回溯112, 104, 111, 110
统计/转换中序 or 反向中序530, 501, 538

🌟 最后送你一句鸡汤:

“树不会说话,但它的结构告诉你一切。”
—— 只要你愿意递归下去,答案终会浮现。