LeetCode 110.平衡二叉树(Java/C/Python3/Go实现含注释说明,Easy)

标签

  • 深度优先搜索
  • 递归

题目描述

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

本题中,一棵高度平衡的二叉树定义为:

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

原题:LeetCode 110.平衡二叉树

思路及实现

方法一:自顶向下递归(纯递归)

思路

定义函数 height\texttt{height}height,用于计算二叉树中的任意一个节点 ppp 的高度:
在这里插入图片描述
为了判断二叉树是否平衡,我们可以采用一个自顶向下的递归方法。该方法通过计算每个节点左右子树的高度差,并与 1 进行比较来判断当前子树是否平衡。如果当前子树平衡,则继续递归检查其子节点。

代码实现
Java版本
class Solution {  
  
    // 判断二叉树是否平衡  
    public boolean isBalanced(TreeNode root) {  
  
        // 如果根节点为空(即树为空),则树是平衡的  
        if (root == null) {  
            return true;  
        }  
        // 否则,判断当前节点的左右子树高度差是否不超过1,并且左右子树都是平衡的  
        else {  
            return Math.abs(height(root.left) - height(root.right)) <= 1 &&   
                   isBalanced(root.left) && // 递归判断左子树是否平衡  
                   isBalanced(root.right);  // 递归判断右子树是否平衡  
        }  
  
    }  
  
    // 计算树的高度  
    public int height(TreeNode root) {  
  
        // 如果根节点为空(即树为空或到达叶子节点的下一层),则返回高度为0  
        if (root == null) {  
            return 0;  
        }  
        // 否则,返回当前节点左子树和右子树中的较大高度加1(当前节点的高度为其子树的最大高度加1)  
        else {  
            return Math.max(height(root.left), height(root.right)) + 1;  
        }  
  
    }  
  
    // TreeNode类(假设已经在外部定义)  
    // class TreeNode {  
    //     int Val;  
    //     TreeNode Left;  
    //     TreeNode Right;  
    //     TreeNode(int val) { Val = val; }  
    // }  
  
}

说明:isBalanced 方法检查树是否平衡,同时递归地计算左右子树的高度。height 方法返回树的高度。

C语言版本
int height(struct TreeNode* root) {
    if (root == NULL) {
        return 0;
    } else {
        return fmax(height(root->left), height(root->right)) + 1;
    }
}

bool isBalanced(struct TreeNode* root) {
    if (root == NULL) {
        return true;
    } else {
        return fabs(height(root->left) - height(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
    }
}

说明:在C语言中,我们使用了fmax函数来计算两个整数中的较大值,并且fabs函数用于计算绝对值。

Python3版本
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def height(root: TreeNode) -> int:
            if not root:
                return 0
            return max(height(root.left), height(root.right)) + 1

        if not root:
            return True
        return abs(height(root.left) - height(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)

说明:在Python中,我们约定如果子树不平衡,则height函数返回-1,这样可以在isBalanced中直接利用返回值进行判断。

Golang版本
package main

import (
	"math"
)

func isBalanced(root *TreeNode) bool {
    if root == nil {
        return true
    }
    return abs(height(root.Left) - height(root.Right)) <= 1 && isBalanced(root.Left) && isBalanced(root.Right)
}

func height(root *TreeNode) int {
    if root == nil {
        return 0
    }
    return max(height(root.Left), height(root.Right)) + 1
}

func max(x, y int) int {
    if x > y {
        return x
    }
    return y
}

func abs(x int) int {
    if x < 0 {
        return -1 * x
    }
    return x
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/balanced-binary-tree/solutions/377216/ping-heng-er-cha-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

说明:在Golang版本中,我们使用了一个辅助函数dfs来进行深度优先搜索,该函数返回子树的高度和是否平衡两个值。如果子树不平衡,则dfs返回-1和false。absmax是辅助函数,分别用于计算绝对值和取两个整数中的较大值。

复杂度分析

  • 时间复杂度
    时间复杂度为 O(n),其中 n 是二叉树中的节点数。这是因为在递归的过程中,我们需要访问每一个节点来计算其左右子树的高度,并且对每个节点都要执行一次判断是否平衡的操作(比较高度差以及递归检查子树是否平衡)。每个节点最多被访问一次,因此总的时间复杂度是线性的。

  • 空间复杂度
    空间复杂度取决于递归调用的深度,也就是树的深度。在最坏的情况下,当树完全不平衡时(例如每个节点都只有左子节点或右子节点),递归的深度会达到树的高度,也就是 O(n)。此时,递归调用栈需要存储 O(n) 个节点信息。

然而,在平均情况下,二叉树是平衡的,其深度通常是对数级别的,即 O(log n)。因此,在平均情况下,空间复杂度是 O(log n)。

但需要注意的是,空间复杂度并不只包括递归调用栈的开销,还包括系统为函数调用分配的其他资源(如局部变量等)。但在判断二叉树是否平衡的问题中,这些开销通常相对较小,可以忽略不计。

  • 总结
    时间复杂度:O(n)
    空间复杂度(最坏情况):O(n)
    空间复杂度(平均情况):O(log n)
    其中,n 是二叉树中的节点数。

方法一:自顶向下递归(带后序遍历)

思路

方法一由于是自顶向下递归,因此对于同一个节点,函数 height 会被重复调用,导致时间复杂度较高。如果使用自底向上的做法,则对于每个节点,函数 height 只会被调用一次。

自底向上递归的做法类似于后序遍历,对于当前遍历到的节点,先递归地判断其左右子树是否平衡,再判断以当前节点为根的子树是否平衡。如果一棵子树是平衡的,则返回其高度(高度一定是非负整数),否则返回 −1-1−1。如果存在一棵子树不平衡,则整个二叉树一定不平衡。

方式二:带后序遍历的递归方法(自底向上)

思路

带后序遍历的递归方法(自底向上)在遍历到每个节点时,首先递归地检查其左右子树是否平衡,并返回子树的高度。如果子树不平衡(即高度差大于1),则立即返回-1表示不平衡,并向上传递这个信息。如果子树平衡,则返回其高度,继续向上传递。这样,在遍历到根节点时,就可以知道整棵树是否平衡。

代码实现

Java版本
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    public boolean isBalanced(TreeNode root) {
        return height(root) != -1;
    }

    private int height(TreeNode node) {
        if (node == null) {
            return 0;
        }
        int leftHeight = height(node.left);
        if (leftHeight == -1) {
            return -1;
        }
        int rightHeight = height(node.right);
        if (rightHeight == -1) {
            return -1;
        }
        if (Math.abs(leftHeight - rightHeight) > 1) {
            return -1;
        }
        return Math.max(leftHeight, rightHeight) + 1;
    }
}

说明:在Java版本中,isBalanced方法调用height方法来判断树是否平衡。height方法递归地计算每个节点的高度,并在发现不平衡时返回-1。

C语言版本
#include <limits.h>

typedef struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
} TreeNode;

int height(TreeNode* node) {
    if (node == NULL) {
        return 0;
    }
    int leftHeight = height(node->left);
    if (leftHeight == -1) {
        return -1;
    }
    int rightHeight = height(node->right);
    if (rightHeight == -1) {
        return -1;
    }
    if (abs(leftHeight - rightHeight) > 1) {
        return -1;
    }
    return MAX(leftHeight, rightHeight) + 1;
}

bool isBalanced(TreeNode* root) {
    return height(root) != -1;
}

说明:在C语言版本中,同样使用height函数来计算节点高度并判断平衡性。注意这里使用了limits.h中的INT_MIN来表示-1的整数类型(但在这个例子中我们直接返回-1),以及自定义的MAX宏来获取两个数中的较大值(或者可以使用#include <stdlib.h>中的max函数,如果存在的话)。

Python3版本
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def isBalanced(root):
    def height(node):
        if not node:
            return 0
        leftHeight = height(node.left)
        if leftHeight == -1:
            return -1
        rightHeight = height(node.right)
        if rightHeight == -1:
            return -1
        if abs(leftHeight - rightHeight) > 1:
            return -1
        return max(leftHeight, rightHeight) + 1
    
    return height(root) != -1

说明:在Python3版本中,我们定义了一个嵌套函数height来计算节点高度,并在isBalanced函数中调用它。Python中没有类型定义,所以我们直接使用类定义来创建树节点。

Golang版本
package main  
  
import (  
	"fmt"  
	"math"  
)  
  
type TreeNode struct {  
	Val   int  
	Left  *TreeNode  
	Right *TreeNode  
}  
  
func isBalanced(root *TreeNode) bool {  
	return height(root) != -1  
}  
  
func height(node *TreeNode) int {  
	if node == nil {  
		return 0  
	}  
	leftHeight := height(node.Left)  
	if leftHeight == -1 {  
		return -1  
	}  
	rightHeight := height(node.Right)  
	if rightHeight == -1 {  
		return -1  
	}  
	if math.Abs(float64(leftHeight-rightHeight)) > 1 {  
		return -1  
	}  
	return int(math.Max(float64(leftHeight), float64(rightHeight))) + 1  
}  
  

复杂度分析

  • 时间复杂度:O(n),其中n是二叉树中的节点数。每个节点最多被访问一次,因此总的时间复杂度是线性的。
  • 空间复杂度:O(h),其中h是二叉树的高度。这是由递归调用栈的深度决定的。在最坏的情况下(树完全不平衡),空间复杂度为O(n)。然而,在平均情况下,二叉树是平衡的,其高度通常是对数级别的,即O(log n)。但需要注意的是,这里的空间复杂度不包括可能由操作系统分配的系统

总结

针对上面提到的自顶向下递归(方式一)和自底向上递归(方式二)的二叉树平衡性检查方法,我们可以进行如下总结:

方式优点缺点时间复杂度空间复杂度
方式一(自顶向下递归)直观易理解,直接根据定义判断可能产生大量重复计算,效率较低O(n)O(h)(h为树的高度,最坏情况下为O(n))
方式二(自底向上递归)利用后序遍历减少重复计算,效率高依赖于递归和高度差的比较,可能难以理解O(n)O(h)(h为树的高度,最坏情况下为O(n))

注意:在方式二中,虽然空间复杂度在最坏情况下为O(n),但这是因为递归调用栈的深度可能达到n。在平均情况下,对于平衡树,空间复杂度为O(log n)。

相似题目

以下是一些与判断二叉树平衡性相关的相似题目,它们涉及树的遍历、深度或高度的计算等概念:

相似题目难度链接
LeetCode 104 二叉树的最大深度简单LeetCode-104
LeetCode 110 平衡二叉树简单LeetCode-110
LeetCode 111 二叉树的最小深度简单LeetCode-111
LeetCode 543 二叉树的直径简单LeetCode-543
LeetCode 124 二叉树中的最大路径和困难LeetCode-124

这些题目都涉及到对二叉树的遍历和深度/高度的计算,可以通过递归或迭代的方式解决。其中,LeetCode 110题目与本文讨论的二叉树平衡性检查最为相似。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/586205.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

羽毛多肽复合纳米纤维膜

羽毛多肽复合纳米纤维膜是一种结合了羽毛多肽和其他纳米纤维材料&#xff08;如P(MA-AA)等&#xff09;的新型生物材料。这种复合纳米纤维膜通过引入羽毛多肽&#xff0c;进一步提升了其生物相容性、生物活性以及吸附性能。 羽毛多肽作为一种天然生物材料&#xff0c;具有良好的…

Centos7+Hadoop3.3.4+KDC1.15+Ranger2.4.0集成

一、集群规划 本次测试采用3台虚拟机&#xff0c;操作系统版本为centos7.6。 kerberos采用默认YUM源安装&#xff0c;版本为&#xff1a;1.15.1-55 Ranger版本为2.4.0 系统用户为ranger:ranger IP地址主机名KDCRanger192.168.121.101node101.cc.localKDC masterRanger Admin…

Spring6 当中的 Bean 循环依赖的详细处理方案+源码解析

1. Spring6 当中的 Bean 循环依赖的详细处理方案源码解析 文章目录 1. Spring6 当中的 Bean 循环依赖的详细处理方案源码解析每博一文案1.1 Bean的循环依赖1.2 singletion 下的 set 注入下的 Bean 的循环依赖1.3 prototype下的 set 注入下的 Bean 的循环依赖1.4 singleton下的构…

MouseBoost PRO for Mac激活版:强大的 鼠标增强软件

在追求高效工作的今天&#xff0c;MouseBoost PRO for Mac成为了许多Mac用户的得力助手。这款功能强大的鼠标增强软件&#xff0c;以其独特的智能化功能和丰富的实用工具&#xff0c;让您的电脑操作更加便捷、高效。 MouseBoost PRO for Macv3.4.0中文激活版下载 MouseBoost PR…

Hotcoin Research | 市场洞察:2024年4月22日-28日

加密货币市场表现 本周内加密大盘整体呈现出复苏状态&#xff0c;在BTC减半后进入到震荡上行周期。BTC在$62000-66000徘徊&#xff0c;ETH在$3100-3300徘徊&#xff0c;随着港交所将于 4 月 30 日开始交易嘉实基金的比特币和以太坊现货 ETF&#xff0c;周末行情有一波小的拉升…

帮助 Python 用户构建 CLI 界面:直观易写、简单高效 | 开源日报 No.240

tiangolo/typer Stars: 13.7k License: MIT typer 是一个构建出色命令行界面&#xff08;CLI&#xff09;的库&#xff0c;基于 Python 类型提示。它旨在让开发者轻松创建用户喜欢使用的 CLI 应用程序。其主要功能和核心优势包括&#xff1a; 直观易写&#xff1a;强大编辑器…

vue3 jspdf,element table 导出excel、pdf,横板竖版分页

多个表格需要&#xff0c;pdf需要的格式与原本展示的表格样式不同 1.创建一个新的表格&#xff0c;设置pdf需要的样式&#xff0c;用vue的h函数放入dom中 2.excel用xlxs插件直接传入新建el-table的dom,直接导出 3.pdf导出类似excel黑色边框白底黑字的文件&#xff0c;把el-t…

FFmpeg开发笔记(二十三)使用OBS Studio开启RTMP直播推流

OBS是一个开源的直播录制软件&#xff0c;英文全称叫做Open Broadcaster Software&#xff0c;广泛用于视频录制、实时直播等领域。OBS不但开源&#xff0c;而且跨平台&#xff0c;兼容Windows、Mac OS、Linux等操作系统。 OBS的官网是https://obsproject.com/&#xff0c;录制…

STM32利用硬件I2C读取MPU6050陀螺仪数据

有了前面的基本配置&#xff0c;这节读取MPU6050的数据还算是简单&#xff0c;主要就是初始化时给MPU6050一些配置&#xff0c;取消睡眠模式&#xff0c;MPU6050开机是默认睡眠模式的&#xff0c;读写无效&#xff0c;所以上来就要先更改配置&#xff1a; MPU6050寄存器初始化…

mongodb卸载(win)

关闭服务 &#xff08;或者cmd卸载服务&#xff1a;&#xff09; net stop 服务名称卸载应用 至此&#xff0c;卸载完成&#xff01;

微隔离实施五步法,让安全防护转起来

前言 零信任的最核心原则→最小权限 安全的第一性原理→预防 零信任的最佳实践→微隔离 “零信任”这个术语的正式出现&#xff0c;公认是在2010年由Forrester分析师John Kindervag最早提出。时至今日&#xff0c;“零信任”俨然已成安全领域最热门的词汇&#xff0c;做安全…

实验报告5-Spring MVC实现页面

实验报告5-SpringMVC实现页面 一、需求分析 使用Spring MVC框架&#xff0c;从视图、控制器和模型三方面实验动态页面。模拟实现用户登录&#xff0c;模拟的用户名密码以模型属性方式存放在Spring容器中&#xff0c;控制器相应用户请求并映射参数&#xff0c;页面收集用户数据或…

设计模式-01 设计模式单例模式

设计模式-01 设计模式单例模式 目录 设计模式-01 设计模式单例模式 1定义 2.内涵 3.使用示例 4.具体代码使用实践 5.注意事项 6.最佳实践 7.总结 1 定义 单例模式是一种设计模式&#xff0c;它确保一个类只能被实例化一次。它通过在类内部创建类的唯一实例并提供一个全…

uniapp + uView动态表单校验

项目需求&#xff1a;动态循环表单&#xff0c;并实现动态表单校验 页面&#xff1a; <u--form label-position"top" :model"tmForm" ref"tmForm" label-width"0px" :rulesrules><div v-for"(element, index) in tmForm…

(详细整理!!!!)Tensorflow与Keras、Python版本对应关系!!!

小伙伴们大家好&#xff0c;不知道大家有没有被tensorflow框架困扰过 今天我就给大家整理一下tensorflow和keras、python版本的对应关系 大家这些都可以在官网找到&#xff0c;下面我把官网的连接给大家放在这里&#xff1a;在 Windows 环境中从源代码构建 | TensorFlow (g…

搭建大型分布式服务(三十七)SpringBoot 整合多个kafka数据源-取消限定符

系列文章目录 文章目录 系列文章目录前言一、本文要点二、开发环境三、原项目四、修改项目五、测试一下五、小结 前言 本插件稳定运行上百个kafka项目&#xff0c;每天处理上亿级的数据的精简小插件&#xff0c;快速上手。 <dependency><groupId>io.github.vipjo…

基于 React 的图形验证码插件

react-captcha-code NPM 地址 &#xff1a; react-captcha-code - npm npm install react-captcha-code --save 如下我自己的封装&#xff1a; import Captcha from "react-captcha-code";type CaptchaType {captchaChange: (captchaInfo: string) > void;code…

前端发起网络请求的几种常见方式(XMLHttpRequest、FetchApi、jQueryAjax、Axios)

摘要 前端发起网络请求的几种常见方式包括&#xff1a; XMLHttpRequest (XHR)&#xff1a; 这是最传统和最常见的方式之一。它允许客户端与服务器进行异步通信。XHR API 提供了一个在后台发送 HTTP 请求和接收响应的机制&#xff0c;使得页面能够在不刷新的情况下更新部分内容…

Flutter笔记:Widgets Easier组件库(2)阴影盒子

Flutter笔记 Widgets Easier组件库&#xff08;2&#xff09;&#xff1a;阴影盒子 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress o…

Python中的动态数据可视化Bokeh库实战

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python 中的动态数据可视化Bokeh库实战 在数据科学和可视化领域&#xff0c;动态数据可视化…