《二叉树刷题计划》——平衡二叉树

简介: 《二叉树刷题计划》——平衡二叉树

再解答平衡二叉树这个问题前,我们先来看这样一个题目

88478be1e30e4701a99248247f707fe8.png📝这个问题我们可以转换为子问题求解,如果我们知道了该二叉树的左子树和右子树的最大深度就相当于知道了该二叉树的深度,同样左子树和右子树也有各自他们所对应的左子树和右子树,这样我们还需要求他们各自的左右子树的深度,这就是一个从上到下的递归过程。


把一个大规模的问题,拆分为多个小规模的子问题

🌰要求3为根节点的二叉树深度:

🌰以9为根节点的二叉树的深度x

🌰以20为根节点的二叉树的深度y

答案=Max(9为根节点的二叉树的深度x,20为根节点的二叉树的深度y)+1

6a78cb84922f4447970eec2913d6797b.png

public int maxDepth(TreeNode root) {
        if (root == null) return 0; // 递归结束的条件,root节点为null,深度自然为0
        int leftHeight = maxDepth(root.left); // 求左子树的高度
        int rightHeight = maxDepth(root.right);  // 求右子树的高度
        //单层逻辑
        //左子树的深度和右子树的深度最大的值+1就是二叉树的深度
        return Math.max(leftHeight, rightHeight) + 1;
    }

有了上面的基础,我们回过头来看我们的平衡二叉树问题

5b0fcca97fe04b49b3e273cf3bb1f7a4.png

 

分析:

二叉树的每个节点的左右子树的高度差的绝对值不超过 1,则二叉树是平衡二叉树。高度差的绝对值不超过1好办(我们上面那个题不就是求高度的吗?)但问题是怎样确保每个结点的左右子树高度差都不超过1呢?

我们不妨换一种思路,如果一个树是平衡二叉树,那么他的左子树和右子树也都必须是平衡二叉树才行。然后如果左子树想是一个平衡二叉树,那么该左子树他的左右子树是不是也必须是平衡二叉树呀!

这样从上往下递归下去,在递归的过程中我们一定是通过求当前结点的左右子树的高度来进行是否是平衡树的判断的,那么在递归过程中我们其实就完成了对每个结点左右子树高度的判断。

class Solution {
    // 求当前结点高度的函数
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        int leftHeight = maxDepth(root.left);
        int rightHeight = maxDepth(root.right);
        return Math.max(leftHeight, rightHeight) + 1;
    }
    // 一个树既然是平衡二叉树,那么他的所有子树都是平衡二叉树
    public boolean isBalanced(TreeNode root) {
        if (root == null) return true;
        int left = maxDepth(root.left); // 递归求当前树左右子树的深度
        int right = maxDepth(root.right);
        if (left - right < -1 || left - right > 1) return false; // 如果差的绝对值大于一就返回false
        return isBalanced(root.left) && isBalanced(root.right); 
        // 递归判断每个节点是否是平衡二叉树,就该结点的左子树和右子树是否为平衡二叉树
        // 只有当左子树和右子树都是平衡二叉树时,该树才是平衡二叉树
    }
}

上面我们对每个结点其实都算了他的左右子树的高度,但很多情况下对每个结点都判断其实是没必要的,比如:

4eaad9282de34566b37c732bee32977e.png

🌰 在这个二叉树中,很明显就不是一个平衡二叉树,比如结点9他的左子树的高度是2,右子树高度是0,这明显就不符合题目中平衡二叉树的定义啊!那么我们能不能在求高度的时候,如果遇到高度差的绝对值大于1了,就直接说明该二叉树不平衡呢


📝所以我们可以对于当前遍历到的节点,先递归地判断其左右子树是否平衡,再判断以当前节点为根的子树是否平衡。如果一棵子树是平衡的,则返回其高度(高度一定是非负整数),否则返回 -1(仅仅只是把不平衡的子树标记起来,只要他与正常的高度返回值不同就行也可以是-100)。如果存在一棵子树不平衡,即返回了负数,则整个二叉树一定不平衡。

class Solution {
    // 求当前结点高度的函数
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        // 在求高度的过程中其实也进行了平衡二叉树的判断,
        // 如果返回值为负数说明已经有一个子树不是平衡二叉树了,即整个树已经不是平衡二叉树了
        int leftHeight = maxDepth(root.left);  // 所以接下来if里要加上leftHeight >= 0 && rightHeight >= 0这样的判断
        int rightHeight = maxDepth(root.right);  // 如果为-1,已经不是正常情况,所以不能进入到接下来的if语句里
        if (leftHeight >= 0 && rightHeight >= 0 && Math.abs(leftHeight - rightHeight) <= 1) {
            return Math.max(leftHeight, rightHeight) + 1; // 正常情况下返回的一定大于0的
        }
        else {
            return -1;  // 说明该子树的不是平衡二叉树,即整个树就不是平衡二叉树
        }
    }
    // 一个树既然是平衡二叉树,那么他的所有子树都是平衡二叉树
    public boolean isBalanced(TreeNode root) {
        return maxDepth(root)>=0; // 只有每个子树都是平衡二叉树,该数才是平衡二叉树
        // 如果每个子树都是平衡的,累加的返回值不可能为负数(因为每个相加的高度都是整数)
        // 如果一个子树返回了-1,及时其他的子树是正常的但因为我们再maxDepth的if语句里加了
        // leftHeight >= 0 && rightHeight >= 0,所以正常的高度也不会返回,也不会把负数冲掉
    }
}

再回顾一下就是:

  • 一是我们需要先处理子树的深度再进行 比较
  • 二是如果我们在处理子树时发现其已经不平衡了,则可以返回一个-1,使得所有其长辈节 点可以避免多余的判断  


相关文章
|
SQL 分布式计算 运维
如何对付一个耗时6h+的ODPS任务:慢节点优化实践
本文描述了大数据处理任务(特别是涉及大量JOIN操作的任务)中遇到的性能瓶颈问题及其优化过程。
|
6月前
|
机器学习/深度学习 数据采集 算法
【风电功率预测】【多变量输入单步预测】基于RVM-Adaboost的风电功率预测研究(Matlab代码实现)
【风电功率预测】【多变量输入单步预测】基于RVM-Adaboost的风电功率预测研究(Matlab代码实现)
379 129
|
人工智能 边缘计算 弹性计算
阿里云连续两年入选沙利文中国企业出海云服务市场领导者梯队
阿里云连续两年综合竞争力位居领导者梯队,是唯一进入领导者梯队的中国云厂商
|
存储 数据采集 监控
SNMP 使用总结
SNMP 使用总结
1758 0
|
存储 运维 数据建模
小白入门之数据建模-以兴趣社区为例
本文作者分享了一些对数据建模的理解,并以社区业务为例展开讨论。
|
存储 缓存 监控
Java中的线程池深度解析####
本文深入探讨了Java并发编程中的核心组件——线程池,从其基本概念、工作原理、核心参数解析到应用场景与最佳实践,全方位剖析了线程池在提升应用性能、资源管理和任务调度方面的重要作用。通过实例演示和性能对比,揭示合理配置线程池对于构建高效Java应用的关键意义。 ####
|
存储 开发框架 .NET
ASP.NET Core SignalR系列之Hub教程
ASP.NET Core SignalR系列之Hub教程
557 0
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
979 1
|
缓存 人工智能 算法
【AI系统】CPU 计算时延
CPU(中央处理器)是计算机系统的核心,其计算时延(从指令发出到完成所需时间)对系统性能至关重要。本文探讨了CPU计算时延的组成,包括指令提取、解码、执行、存储器访问及写回时延,以及影响时延的因素,如时钟频率、流水线技术、并行处理、缓存命中率和内存带宽。通过优化这些方面,可以有效降低计算时延,提升系统性能。文中还通过具体示例解析了时延产生的原因,强调了内存时延对计算速度的关键影响。
471 0
|
人工智能 自然语言处理 搜索推荐
博物馆地图导览系统:GIS与蓝牙定位技术实现地图导览与语音解说功能
维小帮博物馆地图导览系统结合GIS地图、蓝牙定位及智能语音解说,为访客提供沉浸式导览。系统采用自研地图引擎,精准构建三维模型,支持路径规划与个性化定制。蓝牙技术实现高精度室内定位及自动触发语音解说功能,无需手动操作。系统还支持多语言解说与AI语音生成,提升参观体验。目前已在多个博物馆应用并获好评。期待与您共同推进文化科技的融合发展!
718 3