二叉树入门通关指南(一):遍历、对称与深度

79 阅读5分钟

写在前面

二叉树是数据结构中最经典、最常考的非线性结构之一。无论是 LeetCode 刷题、技术面试,还是实际工程中的表达式解析、文件系统建模,几乎都能看到它的身影。

然而,很多初学者在面对“前序、中序、后序、层序”这些术语时容易混淆,写递归时更是频频踩坑:空指针报错、逻辑顺序颠倒、边界条件遗漏……看似简单的题目,一上手就出错。

本系列将从最基础的遍历出发,逐步深入对称性、深度、路径等高频问题,带你真正理解二叉树的递归本质与遍历逻辑

这是第一篇——我们先打好地基。

二叉树的三种深度优先遍历(递归实现)

二叉树的前序、中序和后序遍历都属于深度优先搜索(DFS),区别仅在于访问当前节点的时机不同。以下是它们的标准递归写法。

前序遍历: 根 → 左 → 右

var preorderTraversal = function(root) {
    let result = [];
    const inorder = function(root) {
        if(root === null) return;
        result.push(root.val);
        inorder(root.left);
        inorder(root.right);
    }
    inorder(root);
    return result;
};

image.png

中序遍历(左 → 根 → 右)

var preorderTraversal = function(root) {
    let result = [];
    const inorder = function(root) {
        if(root === null) return;
        inorder(root.left);
        result.push(root.val);
        inorder(root.right);
    }
    inorder(root);
    return result;
};

image.png

后序遍历 左 → 右 → 根

var preorderTraversal = function(root) {
    let result = [];
    const inorder = function(root) {
        if(root === null) return;
        inorder(root.left); 
        inorder(root.right);
        result.push(root.val);
    }
    inorder(root);
    return result;
};

image.png

这三种遍历结构高度相似,唯一区别就是 push 语句的位置。掌握这个规律,就能轻松写出任意一种遍历方式。

提交结果(前序遍历)

image.png


层序遍历:

除了前序、中序和后序这些深度优先的遍历方法外,二叉树还有一种重要的遍历策略:层序遍历

所谓层序遍历,就是按照树的层次结构,从根节点开始,自上而下、每一层从左到右依次访问所有节点。这种方式不沿着某一条路径深入到底,而是“横向”推进,逐层展开。

要高效实现这种遍历,通常需要借助队列这一数据结构。

因为队列遵循“先进先出”的原则,能自然地维持节点访问的顺序:先入队的节点(位于上层或靠左)会先被处理,从而确保遍历按层有序进行。这与使用栈实现的深度优先遍历形成鲜明对比。

值得一提的是,层序遍历本质上就是图论中的广度优先搜索(BFS),只是在二叉树这一特殊结构上的具体体现。

具体代码如下:

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

提交结果: image.png


二叉树的性质:

对称问题

原题在这:101. 对称二叉树 - 力扣(LeetCode) 这道题看起来很简单,当你看到最终代码时,甚至会觉得“不过如此”。

但别小看它——判断逻辑和递归结构非常容易出错,尤其是在面试高压环境下,稍不注意就会踩坑。

先来看一段正确实现:

var isSymmetric = function(root) {
    if(!root) return true;

    const compareNode = function(left,right) {
        if(!left && !right)  return true;
        if(!left || !right) return false;
        if(left.val !== right.val) return false;

        let outside = compareNode(left.left,right.right);
        let inside = compareNode(left.right,right.left);
        return outside && inside;
    }
    return compareNode(root.left,root.right)
};

这段代码虽然短,但每一行都值得细品。下面说两个最容易出错的关键点。

if(!root) return true;

注意!这个边界判断必须放在 isSymmetric 函数里不能放进 compareNode

if(!left && !right)  return true;
if(!left || !right) return false;
if(left.val !== right.val) return false;

你可能会担心:

“第二行 if (!left || !right) 会不会把 leftright 都为 null 的情况误判成 false?”

不会!
因为在执行到第二行之前,第一行已经把“两者都为空”的情况拦截并返回 true 了。
所以第二行实际处理的是:“不是都空,但至少有一个是空”——也就是一个空、一个非空,显然不对称。

如果顺序写反了,比如先写 if (!left || !right) return false,那么当两个节点都为 null 时,会错误地返回 false,导致整个判断失败。

此外,必须在确认 leftright 都非空之后,才能安全访问 .val。否则会触发 Cannot read property 'val' of null 错误。

let outside = compareNode(left.left,right.right);
let inside = compareNode(left.right,right.left);

这是判断二叉树是否对称的核心逻辑,也是这道题的“题眼”

image.png 镜像对称的定义是

左子树的左边 应该等于 右子树的右边
左子树的右边 应该等于 右子树的左边对称二叉树的递归本质是:

“左子树的左” 与 “右子树的右” 对称,且 “左子树的右” 与 “右子树的左” 对称。

求最大、最小深度问题

选一道经典题目:104. 二叉树的最大深度 - 力扣(LeetCode)

var maxDepth = function(root) {
    if (root === null) return 0; // 如果节点为空,则深度为0

    // 计算左子树和右子树的最大深度
    const leftDepth = maxDepth(root.left);
    const rightDepth = maxDepth(root.right);

    // 当前节点的最大深度等于左右子树的最大深度加1
    return Math.max(leftDepth, rightDepth) + 1;
};

基础情况:如果当前节点是 null,则返回深度 0

递归步骤

对于每个节点,分别计算它的左子树和右子树的最大深度。

然后取这两者的较大值,加上当前节点本身(即加 1),得到以该节点为根的子树的最大深度。

最终结果:从根节点开始递归调用此函数,最终会返回整个树的最大深度。

这种方法简洁且高效,利用了递归自然地解决了深度优先搜索的问题,适用于大多数二叉树的深度计算场景


到这里,我们已经掌握了二叉树的四种基本遍历方式,也解决了对称性和最大深度这两个经典问题。你会发现,几乎所有二叉树的递归解法,都建立在“分治 + 子问题返回值”的思想之上

但二叉树的世界远不止于此。

在接下来的文章中,我们将继续探索

基础打牢,进阶不慌。

下一篇,我们不见不散。