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 0return max(height(root.left), height(root.right)) + 1if not root:return Truereturn abs(height(root.left) - height(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)
说明:在Python中,我们约定如果子树不平衡,则
height
函数返回-1,这样可以在isBalanced
中直接利用返回值进行判断。
Golang版本
package mainimport ("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。abs
和max
是辅助函数,分别用于计算绝对值和取两个整数中的较大值。
复杂度分析
-
时间复杂度
时间复杂度为 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 = valself.left = leftself.right = rightdef isBalanced(root):def height(node):if not node:return 0leftHeight = height(node.left)if leftHeight == -1:return -1rightHeight = height(node.right)if rightHeight == -1:return -1if abs(leftHeight - rightHeight) > 1:return -1return max(leftHeight, rightHeight) + 1return 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题目与本文讨论的二叉树平衡性检查最为相似。
相关文章:

LeetCode 110.平衡二叉树(Java/C/Python3/Go实现含注释说明,Easy)
标签 树深度优先搜索递归 题目描述 给定一个二叉树,判断它是否是高度平衡的二叉树。 本题中,一棵高度平衡的二叉树定义为: 一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。 原题:LeetCode 110.平衡二叉树 思路及…...
【SQL】ACID事务与隔离级别
数据库事务 数据库事务具有ACID这4个特性: A:Atomicity,原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;C:Consistency,一致性,事务完…...

深度神经网络中的不确定性研究综述
A.单一确定性方法 对于确定性神经网络,参数是确定的,每次向前传递的重复都会产生相同的结果。对于不确定性量化的单一确定性网络方法,我们总结了在确定性网络中基于单一正向传递计算预测y *的不确定性的所有方法。在文献中,可以找…...
实用的Chrome浏览器命令
Google Chrome 是一款广泛使用的网络浏览器,它提供了许多实用的快捷键和命令,可以帮助用户更高效地浏览网页。以下是一些常用的 Chrome 浏览器命令: 1. 新标签页: Ctrl T (Windows/Linux) 或 Command T (Mac) 2. 关闭当前标签: Ctrl W 或…...
无人作业控制器--4G/5G通信
一、环境 开发环境:ubuntu 22 ros2 humble 发布运行环境:地平线旭日x3派、arm64 4G 模组: 移远EC20模块 5G 模组:移远RG200U-CN 网络通信模组根据需要选择其中一款, 前期我们使用4G模组,后续迭代因为…...

动态规划-两个数组的dp问题2
文章目录 1. 不同的子序列(115)2. 通配符匹配(44) 1. 不同的子序列(115) 题目描述: 状态表示: 根据题意这里的dp数组可以定义为二维,并且dp[i][j]表示字符串t的0到i的…...
如何设置并行度 ——《OceanBase 并行执行》系列 2
并行度(degree of parallelism,简称 DOP),是指在执行过程中所使用的工作线程数量。设计并行执行的初衷在于充分利用多核资源以提升效率。OceanBase 的并行执行框架支持多种设定并行度的方式,既允许用户手动设置&#x…...

python直接发布到网站wordpress之三批量发布图片
在前面的文章中,实现了使用python操作wordpress发布文字内容和图片内容。 python直接发布到网站wordpress之一只发布文字-CSDN博客 python直接发布到网站wordpress之二发布图片-CSDN博客 不过,此时发布图片的数量只能是一张图片。但在实际应用中&…...
C#面:ADO.NET 相对于ADO等主要有什么改进
C# ADO.NET 是微软为.NET平台开发的一套数据访问技术,相对于传统的ADO(ActiveX Data Objects)等,它有以下几个主要改进: 面向对象:ADO.NET 是面向对象的数据访问技术,它使用.NET框架中的类和对…...

web前端学习笔记7-iconfont使用
7. iconfont的使用流程 字体图标使用较多的是阿里巴巴iconfont图标库,它是阿里巴巴体验团队推出的图标库和图标管理平台,提供了大量免费和可定制的矢量图标,以满足网页设计、平面设计、UI设计、应用程序开发和其他创意项目的需求。 官方网站:https://www.iconfont.cn/ 使用…...

国内小白用什么方法充值使用ChatGPT4.0?
首先说一下IOS礼品卡订阅,目前最经济实惠的订阅方式,具体操作步骤 使用IOS设备充值,用 App Stroe 兑换券 1、支付宝地址切换旧金山,在里面买app store 的兑换卷 2、美区Apple ID登陆app store ,充值兑换券 3、IOS设…...
富格林:正确杜绝欺诈实现出金
富格林悉知,现货黄金一直以来都是投资者们追逐的热门品种之一。其安全性和避险特性吸引着广大投资者。但在现货黄金市场中要想实现出金其实并不简单,是需要我们通过一定的技巧和方法去正确杜绝欺诈套路。下面为了帮助广大投资者正确杜绝欺诈实现出金&…...

基于java,SpringBoot和VUE的求职招聘简历管理系统设计
摘要 基于Java, Spring Boot和Vue的求职招聘管理系统是一个为了简化求职者与雇主间互动流程而设计的现代化在线平台。该系统后端采用Spring Boot框架,以便快速搭建具有自动配置、安全性和事务管理等特性的RESTful API服务,而前端则使用Vue.js框架构建动…...
sqlserver数据库日志文件log.ldf文件占用过大清除的办法
sqlserver数据库日志文件log.ldf文件占用过大清除的办法 技术交流 http://idea.coderyj.com/ 1.清除数据库日志的方法 --- 查看数据库日志文件名 USE cs GO SELECT file_id, name,size,* FROM sys.database_files;ps 可以看到其中name字段为数据库日志名称"数据库日志名称…...

【Python小技巧】matplotlib不显示图像竟是numpy惹的祸
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、问题:df.plot() 显示不出图像二、尝试各种解决办法1. 增加matplotlib.use,设定GUI2. 升级matplotlib版本 三、numpy是个重要的库1. …...

【AIGC】1、爆火的 AIGC 到底是什么 | 全面介绍
文章目录 一、AIGC 的简要介绍二、AIGC 的发展历程三、AIGC 的基石3.1 基本模型3.2 基于人类反馈的强化学习3.3 算力支持 四、生成式 AI(Generative AI)4.1 单模态4.1.1 生成式语言模型(Generative Language Models,GLM࿰…...
云计算技术概述_3.云计算的部署方式
根据NIST的定义,云计算从部署模式上看可以分为公有云、社区云、私有云和混合云四种类型。 注:NIST(美国国家标准与技术研究院)是美国商务部下属的一个物理科学实验室和非监管机构。 其任务是促进创新和行业竞争力。 NIST 将其活动…...

简述 BIO 、NIO 模型
BIO : 同步阻塞I/O(Block IO) 服务器实现模式为每一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,此处可以通过线程池机制进行优化。 impo…...
【Python小练】随机验证码
题目 提示输出含数字、字母的四位随机数,输入提示的验证码进行验证,验证码正确结束程序,验证码错误继续输入。 分析 我们可以通过random模块生成0到9的随机数,也可以通过生成65到90的随机数,将65到90的随机ASCLL码转换…...
《1w实盘and大盘基金预测 day30》
今日预测: 3123-3150-3177 探底回升,震荡上涨,收小红小绿 双创指数后期上涨的幅度也是会大于上证的,四月底的时候就提醒建仓。 关注板块:医疗、地产、电力、证券 这周预测 这周上证指数最高看到3200 继续看涨&#…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
CppCon 2015 学习:REFLECTION TECHNIQUES IN C++
关于 Reflection(反射) 这个概念,总结一下: Reflection(反射)是什么? 反射是对类型的自我检查能力(Introspection) 可以查看类的成员变量、成员函数等信息。反射允许枚…...