一、题目描述
给定一个二叉树,判断它是否是平衡二叉树。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:true
示例 2:
输入:root = [1,2,2,3,3,null,null,4,4] 输出:false
示例 3:
输入:root = [] 输出:true
提示:
- 树中的节点数在范围
[0, 5000]
内 -10^4 <= Node.val <= 10^4
二、解题思路
平衡二叉树是指任何节点的两个子树的高度差不超过1的二叉树。我们可以使用后序遍历的方式来解决这个问题,具体步骤如下:
- 对于二叉树的每一个节点,我们都需要计算它的左子树和右子树的高度。
- 如果当前节点的左子树和右子树的高度差大于1,则这个二叉树不是平衡二叉树,直接返回false。
- 如果当前节点的左子树或右子树不是平衡二叉树,则整个二叉树也不是平衡二叉树,直接返回false。
- 如果所有节点都满足平衡二叉树的条件,则返回true。
三、具体代码
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
// 计算左子树的高度
int leftHeight = treeHeight(root.left);
// 计算右子树的高度
int rightHeight = treeHeight(root.right);
// 如果当前节点的左子树和右子树的高度差大于1,则这个二叉树不是平衡二叉树
if (Math.abs(leftHeight - rightHeight) > 1) {
return false;
}
// 递归判断左子树和右子树是否是平衡二叉树
return isBalanced(root.left) && isBalanced(root.right);
}
// 计算二叉树的高度
private int treeHeight(TreeNode root) {
if (root == null) {
return 0;
}
// 计算左子树的高度
int leftHeight = treeHeight(root.left);
// 计算右子树的高度
int rightHeight = treeHeight(root.right);
// 返回当前节点的高度
return Math.max(leftHeight, rightHeight) + 1;
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
-
计算单个节点高度的时间复杂度: 对于每个节点,我们需要计算其左右子树的高度。这是一个递归过程,每次递归都会涉及到访问节点的左右子节点。因此,对于每个节点,我们都需要进行一次递归调用,其时间复杂度为 O(1)。
-
计算整棵树高度的时间复杂度: 在最坏的情况下,树是一个高度不平衡的线性链(即每个节点都只有一个子节点),这时计算高度的递归函数会被调用 n 次(n 是树中节点的数量)。因此,计算整棵树高度的时间复杂度是 O(n)。
-
判断平衡二叉树的时间复杂度: 判断平衡二叉树的过程也是一个递归过程,它会遍历树中的每个节点,并且每次都会调用计算高度的递归函数。因此,总的时间复杂度是 O(n) * O(n) = O(n^2)。
-
综上所述,代码的时间复杂度是 O(n^2)。
2. 空间复杂度
-
计算单个节点高度的空间复杂度: 空间复杂度主要取决于递归调用栈的深度,这在最坏的情况下与树的高度相同。因此,对于单个节点的空间复杂度是 O(h),其中 h 是树的高度。
-
计算整棵树高度的空间复杂度: 由于我们需要对每个节点进行一次递归调用,所以整棵树高度的空间复杂度是 O(n) * O(h) = O(n * h),其中 n 是树中节点的数量,h 是树的高度。
-
判断平衡二叉树的空间复杂度: 判断平衡二叉树的空间复杂度同样取决于递归调用栈的深度,最坏情况下与树的高度相同。因此,总的空间复杂度是 O(h)。
-
综上所述,代码的空间复杂度是 O(h),在最坏情况下是 O(n)。
五、总结知识点
-
递归:这是一种常用的算法设计技巧,用于解决分而治之的问题。在这个问题中,我们使用递归函数来遍历二叉树的每个节点,并计算子树的高度。
-
二叉树遍历:代码中使用的是后序遍历(左-右-根)的方式来访问二叉树的节点。这种遍历方式适合于需要先处理子节点再处理父节点的情况。
-
二叉树的高度:二叉树的高度是从根节点到最远叶子节点的最长路径上的节点数。这个概念在判断平衡二叉树时非常重要。
-
绝对值:
Math.abs()
函数用于计算一个数的绝对值,在这里用于计算左右子树高度差值的绝对值,以判断是否超过了1。 -
逻辑与操作符(&&):用于连接两个布尔表达式,只有当两个表达式都为真时,整个表达式才为真。在代码中,用于判断左右子树是否都是平衡的。
-
私有函数:
treeHeight
函数被标记为private
,这意味着它只能在同一个类中被访问,这是封装的一种形式。 -
递归基:在
treeHeight
函数中,当遇到空节点时,返回高度为0,这是递归的基 case,用于终止递归。 -
函数调用栈:递归函数使用系统栈来存储每一层递归的信息。在递归过程中,每进入一层递归,就会有一个新的函数帧被压入栈中,当递归基被触发时,这些帧会依次弹出。
-
树的结构:代码中使用了
TreeNode
类来表示树的节点,这是二叉树数据结构的基本表示方法。 -
Math.max() 函数:用于返回两个值中的最大值,在这里用于计算当前节点的高度,即左右子树高度的最大值加1。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。