力扣110.平衡二叉树-递归

📋 问题描述

给定一个二叉树,判断它是否是高度平衡的二叉树。

平衡二叉树 的定义:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

💡 解题思路

1. 理解平衡二叉树

平衡二叉树不仅仅是根节点的左右子树高度差不超过1,而是每个节点都必须满足这个条件。

关键点

  • 空树是平衡的

  • 每个节点都需要检查

  • 需要同时获取子树的高度和平衡状态

🔍 多种解法

解法1:自顶向下递归(朴素解法)

java 复制代码
class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) return true;
        
        // 检查当前节点是否平衡
        int leftHeight = height(root.left);
        int rightHeight = height(root.right);
        boolean currentBalanced = Math.abs(leftHeight - rightHeight) <= 1;
        
        // 递归检查左右子树
        return currentBalanced && isBalanced(root.left) && isBalanced(root.right);
    }
    
    private int height(TreeNode root) {
        if (root == null) return 0;
        return Math.max(height(root.left), height(root.right)) + 1;
    }
}

复杂度分析

  • 时间复杂度:O(n²) - 每个节点都要计算高度,而高度计算本身也是O(n)

  • 空间复杂度:O(n) - 递归栈深度

缺点:存在大量重复计算

解法2:自底向上递归(最优解)

java 复制代码
class Solution {
    public boolean isBalanced(TreeNode root) {
        return dfs(root) != -1;
    }
    
    private int dfs(TreeNode node) {
        if (node == null) {
            return 0;
        }
        
        // 递归获取左子树高度
        int h_left = dfs(node.left);
        if (h_left == -1) {
            return -1;
        }
        
        // 递归获取右子树高度
        int h_right = dfs(node.right);
        if (h_right == -1) {
            return -1;
        }
        
        // 检查当前节点是否平衡
        if (Math.abs(h_left - h_right) > 1) {
            return -1;
        }
        
        // 返回当前节点的高度
        return Math.max(h_left, h_right) + 1;
    }
}

复杂度分析

  • 时间复杂度:O(n) - 每个节点只访问一次

  • 空间复杂度:O(h) - 递归栈深度,h为树的高度

优点

  • 一次遍历同时完成高度计算和平衡判断

  • 避免重复计算

  • 尽早发现不平衡情况并终止递归

解法3:迭代法(避免递归栈溢出)

java

复制代码
import java.util.*;

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) return true;
        
        Map<TreeNode, Integer> heights = new HashMap<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        
        // 后序遍历
        TreeNode lastVisited = null;
        TreeNode node = root;
        
        while (!stack.isEmpty() || node != null) {
            if (node != null) {
                stack.push(node);
                node = node.left;
            } else {
                TreeNode peek = stack.peek();
                if (peek.right != null && peek.right != lastVisited) {
                    node = peek.right;
                } else {
                    TreeNode curr = stack.pop();
                    
                    int leftHeight = heights.getOrDefault(curr.left, 0);
                    int rightHeight = heights.getOrDefault(curr.right, 0);
                    
                    if (Math.abs(leftHeight - rightHeight) > 1) {
                        return false;
                    }
                    
                    heights.put(curr, Math.max(leftHeight, rightHeight) + 1);
                    lastVisited = curr;
                }
            }
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度:O(n)

  • 空间复杂度:O(n)

适用场景:树非常深,递归可能导致栈溢出时使用

📊 算法对比表

方法 时间复杂度 空间复杂度 代码复杂度 适用场景 推荐指数
自顶向下递归 O(n²) O(n) 简单 小规模数据 ⭐⭐
自底向上递归 O(n) O(h) 简单 通用场景 ⭐⭐⭐⭐⭐
迭代法 O(n) O(n) 中等 避免栈溢出 ⭐⭐⭐

🎯 面试技巧

1. 问题分析步骤

text

复制代码
1. 确认平衡二叉树的定义
2. 思考暴力解法(自顶向下)
3. 分析暴力解法的缺点(重复计算)
4. 优化思路(自底向上,一次遍历)
5. 实现优化解法

2. 代码实现要点

  • 使用-1表示不平衡状态

  • 尽早返回,减少不必要的递归

  • 注意空树是平衡的特殊情况

3. 复杂度分析

  • 时间:O(n) - 每个节点访问一次

  • 空间:O(h) - 递归栈深度,最坏情况O(n)

4. 常见Follow-up问题

  1. Q :如果树非常大,递归会栈溢出怎么办?
    A:可以使用迭代版本,用栈模拟递归。

  2. Q :能写出非递归版本吗?
    A:使用后序遍历的迭代实现,同时记录节点高度。

  3. Q :如何测试你的算法?
    A:测试用例应包括:空树、单节点树、完全平衡树、完全不平衡树、随机树。

💡 实战练习

练习1:修改问题 - 返回不平衡的节点

java

复制代码
public TreeNode findUnbalancedNode(TreeNode root) {
    // 修改dfs函数,返回不平衡节点
    // 练习:实现这个函数
}

练习2:计算树的最大深度差

java

复制代码
public int maxHeightDifference(TreeNode root) {
    // 返回树中任意节点左右子树的最大高度差
    // 练习:实现这个函数
}

📝 总结

判断平衡二叉树 是一个经典的二叉树问题,考察对递归、树遍历和算法优化的理解。自底向上递归解法是最优解,兼具效率高和代码简洁的优点。

关键收获

  1. 平衡二叉树要求每个节点都满足平衡条件

  2. 自底向上的思路可以避免重复计算

  3. 使用特殊返回值(如-1)可以同时传递高度和状态信息

  4. 递归和迭代可以互相转换,各有适用场景

掌握这个问题不仅有助于解决LeetCode 110,还能加深对树相关算法的理解,为更复杂的树问题打下基础。

🔗 相关题目

相关推荐
sheeta199819 小时前
LeetCode 每日一题笔记 日期:2025.03.21 题目:3643.垂直翻转子矩阵
笔记·leetcode·矩阵
风吹乱了我的头发~21 小时前
Day52:2026年3月20日打卡
算法
2401_831824961 天前
基于C++的区块链实现
开发语言·c++·算法
We་ct1 天前
LeetCode 918. 环形子数组的最大和:两种解法详解
前端·数据结构·算法·leetcode·typescript·动态规划·取反
愣头不青1 天前
238.除了自身以外数组的乘积
数据结构·算法
人工智能AI酱1 天前
【AI深究】逻辑回归(Logistic Regression)全网最详细全流程详解与案例(附大量Python代码演示)| 数学原理、案例流程、代码演示及结果解读 | 决策边界、正则化、优缺点及工程建议
人工智能·python·算法·机器学习·ai·逻辑回归·正则化
WangLanguager1 天前
逻辑回归(Logistic Regression)的详细介绍及Python代码示例
python·算法·逻辑回归
m0_518019481 天前
C++与机器学习框架
开发语言·c++·算法
一段佳话^cyx1 天前
详解逻辑回归(Logistic Regression):原理、推导、实现与实战
大数据·算法·机器学习·逻辑回归
qq_417695051 天前
C++中的代理模式高级应用
开发语言·c++·算法