【每日算法】LeetCode 226. 翻转二叉树

对前端开发者而言,学习算法绝非为了"炫技"。它是你从"页面构建者"迈向"复杂系统设计者"的关键阶梯。它将你的编码能力从"实现功能"提升到"设计优雅、高效解决方案"的层面。从现在开始,每天投入一小段时间,结合前端场景去理解和练习,你将会感受到自身技术视野和问题解决能力的质的飞跃。------ 算法:资深前端开发者的进阶引擎

LeetCode 226. 翻转二叉树

1. 题目描述

LeetCode 226. 翻转二叉树要求我们翻转一棵二叉树,即交换每个节点的左右子树。这个问题看似简单,但它是理解递归和树遍历的经典入门题,对于前端开发者处理DOM树或组件树有直接借鉴意义。

示例

  • 输入:

    复制代码
       4
     /   \
    2     7

    / \ /
    1 3 6 9

  • 输出:

    复制代码
       4
     /   \
    7     2

    / \ /
    9 6 3 1

二叉树节点定义(在JavaScript中):

javascript 复制代码
function TreeNode(val, left, right) {
    this.val = (val===undefined ? 0 : val);
    this.left = (left===undefined ? null : left);
    this.right = (right===undefined ? null : right);
}

2. 问题分析

翻转二叉树的核心是遍历每个节点并交换其左右子节点。这涉及到二叉树的遍历,常见方法有递归和迭代。作为前端开发者,我们可以类比到DOM树的遍历或React/Vue组件树的处理,例如在UI中反转子组件顺序。

关键点:

  • 二叉树可能为空(根节点为null),需要处理边界情况。
  • 翻转是镜像操作,适用于任何二叉树结构(包括不平衡树)。
  • 时间复杂度与节点数相关,空间复杂度取决于遍历方式。

3. 解题思路

解决翻转二叉树主要有两种思路:递归和迭代。递归方法直观简洁,符合分治思想;迭代方法模拟递归过程,避免栈溢出风险。两种方法都是最优解,时间复杂度为O(n),其中n是节点数,因为每个节点访问一次。

  • 最优解:递归和迭代在时间复杂度上相同,但递归更简洁,易于理解;迭代在树深度大时更安全。前端场景中,递归适用于组件树递归渲染,迭代适用于性能敏感操作。

3.1 递归思路

从根节点开始,递归地翻转左子树和右子树,然后交换左右子节点。这类似于深度优先搜索(DFS)。

3.2 迭代思路

使用栈或队列模拟递归过程。栈实现深度优先遍历,队列实现广度优先遍历(BFS)。交换操作在遍历每个节点时进行。

4. 各思路代码实现

以下是基于JavaScript的代码实现,前端开发者可轻松集成到项目或算法练习中。

4.1 递归实现

递归方法直接模拟翻转过程,代码简洁。

javascript 复制代码
var invertTree = function(root) {
    // 基础情况:如果节点为空,直接返回
    if (root === null) {
        return null;
    }
    // 递归翻转左子树和右子树
    const left = invertTree(root.left);
    const right = invertTree(root.right);
    // 交换左右子节点
    root.left = right;
    root.right = left;
    return root;
};

4.2 迭代实现(使用栈)

迭代方法通过栈来模拟递归,避免递归调用栈溢出。

javascript 复制代码
var invertTree = function(root) {
    if (root === null) return null;
    const stack = [root]; // 使用栈存储节点
    while (stack.length > 0) {
        const node = stack.pop();
        // 交换当前节点的左右子节点
        [node.left, node.right] = [node.right, node.left];
        // 将子节点压入栈,继续处理
        if (node.left !== null) stack.push(node.left);
        if (node.right !== null) stack.push(node.right);
    }
    return root;
};

4.3 迭代实现(使用队列)

队列实现广度优先遍历,适合层次处理树。

javascript 复制代码
var invertTree = function(root) {
    if (root === null) return null;
    const queue = [root]; // 使用队列存储节点
    while (queue.length > 0) {
        const node = queue.shift();
        // 交换当前节点的左右子节点
        [node.left, node.right] = [node.right, node.left];
        // 将子节点加入队列
        if (node.left !== null) queue.push(node.left);
        if (node.right !== null) queue.push(node.right);
    }
    return root;
};

5. 各实现思路的复杂度、优缺点对比表格

以下表格总结了不同实现方式的性能特点和适用场景,帮助前端开发者根据实际需求选择。

实现方式 时间复杂度 空间复杂度 优点 缺点 前端应用场景
递归 O(n) O(h),其中h是树高,最坏情况O(n) 代码简洁,易于理解和实现;符合函数式编程思想 递归深度大时可能导致栈溢出;调试可能较复杂 处理嵌套组件树、递归渲染UI(如React递归组件)
迭代(栈) O(n) O(n),栈存储节点 避免递归栈溢出;控制流清晰,适合深度遍历 需要额外空间;代码稍复杂 DOM树深度遍历、状态管理中的树操作
迭代(队列) O(n) O(n),队列存储节点 层次遍历,适合广度优先处理;避免栈溢出 空间开销可能较大;交换顺序可能影响性能 UI布局反转、层级数据处理(如树形菜单)
  • 时间复杂度:所有方法都是O(n),因为每个节点访问一次。
  • 空间复杂度:递归为O(h),取决于树高;迭代为O(n),在最坏情况下存储所有节点。
  • 最优解选择:对于大多数前端场景,递归是首选,因为代码可读性高;在树深度未知或较大时,迭代更安全。
相关推荐
无名无姓某罗2 小时前
jQuery 请求 SpringMVC 接口返回404错误排查
前端·spring·jquery
落羽的落羽2 小时前
【C++】深入浅出“图”——图的遍历与最小生成树算法
linux·服务器·c++·人工智能·算法·机器学习·深度优先
霁月的小屋2 小时前
Vue响应式数据全解析:从Vue2到Vue3,ref与reactive的实战指南
前端·javascript·vue.js
李少兄2 小时前
深入理解 Java Web 开发中的 HttpServletRequest 与 HttpServletResponse
java·开发语言·前端
holidaypenguin2 小时前
antd 5 + react 18 + vite 7 升级
前端·react.js
小满zs2 小时前
Next.js第十五章(Image)
前端·next.js
txp玩Linux2 小时前
rk3568上webrtc处理稳态噪声实践
算法·webrtc
tangbin5830852 小时前
iOS Swift 可选值(Optional)详解
前端·ios
CoovallyAIHub2 小时前
从空地对抗到空战:首个无人机间追踪百万级基准与时空语义基线MambaSTS深度解析
深度学习·算法·计算机视觉