Skip to content

1. 万能递归模版 (DFS - Divide & Conquer)

适用场景:最大深度、直径、最大路径和、翻转二叉树、LCA。
核心心法:后序遍历 (PostOrder)。先递归左右子树拿结果,再汇总当前层。

你做过的「二叉树的直径」和「最大路径和」就是典型。

dfs模版:

js
 */
var invertTree = function(root) {
    if(!root) return null;

    const dfs = (node) => {
        [node.left, node.right] = [node.right, node.left];

        if(node.left) dfs(node.left);
        if(node.right) dfs(node.right);
    }

    dfs(root);
    return root
};

模版代码 (以求高度为例):

分治模版:

js
var maxDepth = function(root) {
    if (!root) return 0; // Base Case

    // 1. 问左右子树要结果 (Divide)
    const left = maxDepth(root.left);
    const right = maxDepth(root.right);

    // 2. 处理当前层逻辑 (Conquer)
    // 比如:我的高度 = max(左, 右) + 1
    return Math.max(left, right) + 1;
};

进阶技巧 (全局变量法):很多 Hard 题(如最大路径和)的特点是,函数返回给父节点的「单边路径」和题目要求的「全局最大拐弯路径」不一样。
解决方法:设一个全局 maxRes,递归过程中不断更新它,但函数最后 return 给上一层的还是单边值。

2. 层序遍历模版 (BFS - Queue)

适用场景:层序遍历、二叉树右视图、层平均值、最小深度。
核心心法:队列 + size 锁定。用 size 锁死当前层节点数,处理完这一层再进入下一层。

你做过的「二叉树的层序遍历」和「右视图」都是这个。

模版代码:

js
var levelOrder = function(root) {
    if (!root) return [];
    const queue = [root];
    const res = [];

    while (queue.length) {
        const len = queue.length; // 锁死当前层的节点数
        const currentLevel = [];

        for (let i = 0; i < len; i++) {
            const node = queue.shift();
            currentLevel.push(node.val);

            // 扩展下一层
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
        res.push(currentLevel);
    }
    return res;
};

3. BST 中序遍历模版 (Inorder Traversal)

适用场景:验证二叉搜索树、二叉搜索树中第 K 小的元素、将有序数组转为 BST。
核心心法:BST 的中序遍历是有序数组。看到 BST 就条件反射想中序遍历。

你未做的「将有序数组转换为二叉搜索树」其实就是这个模版的逆向操作(二分法构造)。

模版代码 (迭代版,常用于第 K 小 / 验证):

js
var isValidBST = function(root) {
    const stack = [];
    let curr = root;
    let prev = -Infinity; // 记录前一个节点的值

    while (curr || stack.length) {
        // 1. 一路向左
        while (curr) {
            stack.push(curr);
            curr = curr.left;
        }

        // 2. 处理当前节点 (中)
        curr = stack.pop();

        // 核心逻辑区域:比如验证是否递增
        if (curr.val <= prev) return false;
        prev = curr.val;

        // 3. 转向右边
        curr = curr.right;
    }
    return true;
};

4. 前缀和模版 (Path Sum Prefix)

适用场景:路径总和 III。
核心心法:二叉树 + Two Sum 的结合体。题目问有多少条路径和为 target,路径不需要从根开始。

暴力双重递归是 O(N^2)。用前缀和 (Map) 可以优化到 O(N),面试高频考点。

模版代码 (针对“路径总和 III”):

js
var pathSum = function(root, targetSum) {
    // key: 前缀和, value: 该前缀和出现的次数
    const map = new Map();
    map.set(0, 1); // 默认前缀和为 0 的有 1 条(啥也不选)
    let count = 0;

    const dfs = (node, curSum) => {
        if (!node) return;

        // 1. 更新当前路径前缀和
        curSum += node.val;

        // 2. 核心公式:找 (curSum - target) 存在过没?
        // 如果存在,说明中间有一段子路径和等于 target
        if (map.has(curSum - targetSum)) {
            count += map.get(curSum - targetSum);
        }

        // 3. 记录当前前缀和,继续递归
        map.set(curSum, (map.get(curSum) || 0) + 1);

        dfs(node.left, curSum);
        dfs(node.right, curSum);

        // 4. 回溯:离开这个节点前,要把这个路径和删掉
        // 因为这只能给子节点用,不能给兄弟节点用
        map.set(curSum, map.get(curSum) - 1);
    };

    dfs(root, 0);
    return count;
};

5. 突击建议

从前序与中序遍历序列构造二叉树:
思路:preorder 第一个是根,拿着这个根去 inorder 里切一刀,左边是左子树,右边是右子树,然后递归。
难点:计算切分后的下标边界。

二叉树展开为链表:
用后序遍历 (Right -> Left -> Root)。
用一个全局变量 prev 记录上一个处理的节点,把当前节点的 right 指向 prev,left 设为 null。

路径总和 III:
直接背诵上面的「前缀和模版」即可。

本站访客数 人次 本站总访问量