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

embedded/2024/10/19 23:37:02/

标签

  • 深度优先搜索
  • 递归

题目描述

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

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

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

原题:LeetCode 110.平衡二叉树

思路及实现

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

思路

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

代码实现
Java版本
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。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版本
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题目与本文讨论的二叉树平衡性检查最为相似。


http://www.ppmy.cn/embedded/26342.html

相关文章

Redis的事务机制能保证ACID属性吗?

目录 事务 ACID 属性 用户如何开启Redis的事务&#xff1f; 使用redis-cli客户端来展示 ​Go语言编码使用事务 Redis 的事务机制能保证哪些属性&#xff1f; 1. 原子性 语法错误 运行错误 执行EXEC时&#xff0c;Redis发生故障 Redis对事务原子性属性的保证情况 2. 一…

vscode相关插件使用----持续更新中

一、VSCode&#xff08;Visual Studio Code&#xff09;常用的插件&#xff1a; Vetur/Volar&#xff1a;用于Vue.js项目的开发&#xff0c;提供语法高亮、智能感知等功能。ESLint&#xff1a;用于JavaScript代码的语法检查和风格检查&#xff0c;支持多种编码规范。Prettier …

安防监控/智能分析EasyCVR视频汇聚平台海康/大华/宇视摄像头国标语音GB28181语音对讲配置流程

一、背景分析 近年来&#xff0c;国内视频监控应用发展迅猛&#xff0c;系统接入规模不断扩大&#xff0c;涌现了大量平台提供商&#xff0c;平台提供商的接入协议各不相同&#xff0c;终端制造商需要给每款终端维护提供各种不同平台的软件版本&#xff0c;造成了极大的资源浪…

Flutter运行项目一直:running gradle task

大体原因就是访问国外的资源由于网络等原因导致访问失败&#xff0c;解决方法就是换成国内的源 修改项目的android/build.gradle 文件&#xff0c;将里面的 google() mavenCentral()替换为 maven {allowInsecureProtocol trueurl https://maven.aliyun.com/repository/googl…

3122.使矩阵满足条件的最少操作次数

周赛第三题,知道要用动态规划,但是不知道怎么回到子问题 显然根据题意我们需要让每一列都相同,但是相邻列不能选择同一种数字,观察到数据nums[i]介于0-9,我们就以此为突破口. 首先我们用count[n][10], count[i][j]记录第i1列值为j的元素个数,转移方程如下: dfs(i,pre) max(dfs…

git变更远端仓库名之后如何修改本地仓库配置的另一种方法?(删remote指针、添加、绑定master)

背景 如果某个远端的仓库地址变化后&#xff0c;本地仓库可以修改对应的remote。 之前谈过几种方法&#xff0c;比如重新设置一个新的remote的指针&#xff0c;绑定到新地址。然后删除origin&#xff0c;然后把新指针mv到origin。比如直接seturl修改&#xff08;git remote se…

泰勒创造力达到顶峰?(上)

hello,大家好&#xff01;今天看一篇经济学人的一篇评论&#xff0c;说的是泰勒斯威夫特当前的创造力。经济学人总是语不惊人死不休&#xff0c;看看它对这位音乐天才做了怎样的评价。 事先声明哈&#xff0c;本文就是一种英语学习类讲述&#xff0c;没带任何个人色彩&#xff…

AI大模型探索之路-训练篇5:大语言模型预训练数据准备-词元化

系列文章目录&#x1f6a9; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据…