二叉树:前中后序、层序遍历-递归/非递归版本、层序遍历、路径和

Posted by CodingWithAlice on March 12, 2025

二叉树:前中后序、层序遍历-递归/非递归版本、层序遍历、路径和

class TreeNode { // 创建节点
    constructor(value, left, right) {
        this.val = value || 0;
        this.left = left || null;
        this.right = right || null; }
}

系统梳理二叉树常见遍历方式

分类方式1:按遍历顺序

  • 深度优先搜索 DFS【访问根节点的时机不同
    • 前序遍历:根节点-左子树-右子树
    • 中序遍历:左子树-根节点-右子树
    • 后序遍历:左子树-右子树-根节点
  • 广度优先搜索 BFS
    • 层序遍历:借助【队列】实现(从上到下,从左到右访问节点)

分类方式2:按实现方式

  • 递归:代码简洁,但可能栈溢出
  • 非递归:使用【栈/队列】实现,适合大量数据

分类方式3:特殊遍历

  • 左视图:获取每层第一个节点,在层序遍历上修改
  • 路径和:回溯算法,一般通过递归实现
let insertLNode = (root, value) => { // 插入左侧,并返回
    if(value){ root.left = new TreeNode(value); return root.left; }
}
let insertRNode = (root, value) => { // 插入右侧,并返回
    if(value){ root.right = new TreeNode(value); return root.right; }
}
// 二叉树创建函数 - 方便调试
let createTree = function (nodes) {
    let root = new TreeNode(nodes[0]); // 第一项为根节点
    let result = [], i = 1;
    result.push(root);
    while (result.length !== 0) {
        let node = result.shift();
        if (node) {
            if (i < nodes.length) {
                result.push(insertLNode(node, nodes[i])); // 左节点
                result.push(insertRNode(node, nodes[i + 1])); // 右节点
            }
            i += 2;
        }
    }
    return root
}
const example = createTree([1, 2, 4, 23, null, 23, 45, 3, 124, 346, null, 1, 0, 90, null]);

一、中序遍历、前序遍历、后续遍历

1、递归实现

// 前序:根-左-右。遍历到这个节点就取值 123
function preOrder(root) {
    if(!root) return [];
    return [root.val, ...preOrder(root.left), ...preOrder(root.right)]
}
// 中序:左-根-右。先深入到左侧叶子节点再取值 213
function inOrder(root) {
    if(!root) return [];
    return [...inOrder(root.left), root.val, ...inOrder(root.right)]
}
// 后序:左-右-根。先左叶子后右叶子最后根节点 231
function postOrder(root) {
    if(!root) return [];
    return [...postOrder(root.left), ...postOrder(root.right), root.val]
}

2、非递归实现:借用栈实现

前序:根-左-右 123

function preOrder(root) {
    if(!root) return [];
    const result = [];
    const stack = [root];
    while(stack.length) {
        const node = stack.pop(); // 尾部取出
        result.push(node.val);
        if(node.right) { stack.push(node.right) }
        if(node.left) { stack.push(node.left) }
    }
    return result;
}

中序:左-根-右 213

function inOrder(root) {
    const result = [];
    const stack = [];
    let current = root;
    while(stack.length || current) {
        while(current) {
            stack.push(current);
            current = current.left;
        }
        current = stack.pop();
        result.push(current.val);
        current = current.right;
    }
    return result
}

后序:左-右-根 231

function postOrder(root) {
    if(!root) return [];
    const result = [];
    const stack = [root];
    while(stack.length) {
        const node = stack.pop();
        result.unshift(node.val); // 和 前序遍历的差异1 从 pop 改为 unshift
        if(node.left) { stack.push(node.left) }
        if(node.right) { stack.push(node.right) } // 和 前序遍历的差异2 先左后右
    }
    return result;
}

二、层序遍历

  • 基于队列的 BFS【推荐】
  • 基于递归的 DFS

1、基于队列的 BFS

// 实现方式:BFS广度优先 + 队列
function levelOrder(root) {
    if (!root) return [];
    const queue = [root]; // 队列操作 shift + push
    const result = [];
    while (queue.length) {
        const levelSize = queue.length;
        const currentLevel = [];
        for (let i = 0; i < levelSize; i++) {
            const node = queue.shift();
            currentLevel.push(node.val); // 当前层
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
        result.push(currentLevel);
    }
    return result;
}

2、基于递归的 DFS - leetcode:102 题

img

给你二叉树的根节点 root 返回其节点值的 层序遍历 
// 示例 1:
输入root = [3,9,20,null,null,15,7]
输出[[3],[9,20],[15,7]]
// 示例 2:
输入root = [1]
输出[[1]]
// 示例 3:
输入root = []
输出[]
// 实现方式:DFS深度优先搜索 + 递归
function levelOrder(root) {
    const result = [];
    if(root) {
        FindLayer(root, result, 0);
    }
    return result;

    function FindLayer(node, result, index) {
        // 层级记录 - index 是层级
        if(Array.isArray(result[index])) {
            result[index].push(node.val);
        } else {
            result[index] = [node.val]
        }
        // 进入下一层
        let currentIndex = index + 1;
        if(node.left) {
            FindLayer(node.left, result, currentIndex);
        }
        if(node.right) {
            FindLayer(node.right, result, currentIndex);
        }
    }
};

三、特殊遍历:左视图

// 层次遍历基础上修改
function leftView(root) {
    if (!root) return [];
    let res = [];
    let queue = [root];
    while (queue.length) {
        let len = queue.length;
        for (let i = 0; i < len; i++) {
            let node = queue.shift();
            if (i === 0) { res.push(node.value) } // 记录每层的第一个节点
            if (node.left) { queue.push(node.left) }
            if (node.right) { queue.push(node.right) }
        }
    }
    return res
}

四、特殊遍历:二叉树中和为目标值的路径

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

img

// 示例 1
输入root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出[[5,4,11,2],[5,8,4,5]]
// 示例 2:
输入root = [1,2,3], targetSum = 5
输出[]
// 示例 3:
输入root = [1,2], targetSum = 0
输出[]
function pathSum(root, target) {
    const result = [];
    function backtrack(node, target, path) {
        if (!node) return;
        path.push(node.val);
        if (!node.left && !node.right && target === node.val) {
            result.push([...path]); // 走到叶子节点 + 找到一条路径
        }
        backtrack(node.left, target - node.val, path); // 递归左子树
        backtrack(node.right, target - node.val, path); // 递归右子树
        path.pop(); // 回溯
    }
    backtrack(root, target, []);
    return result;
}