「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战」
依照惯例,今天我又带来了算法。下面让我们来看看这道题。
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
-
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
输入:root = [3,9,20,null,null,15,7] 输出:true
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
也就是说,只有当一个二叉树的所有子树也都是平衡二叉树的时候,这个二叉树才是平衡二叉树,因此,我们可已通过判断每个子树来判断该二叉树是不是平衡二叉树,这时我们就有两种方法来判断:
- 自顶向下的递归
- 自下至上的递归
自顶向下的递归
这个方法是获取当前结点的最大深度,通过比较此子树的左右子树的最大高度差来判断此子树是否是二叉平衡树。
-
判断空值,如果root==null,直接返回true
-
所有子树必须都满足平衡树性质。
- 判断当前子树是否为平衡树
- 判断当前左子树
- 判断当前右子树
-
root为空时,返回0
-
返回最大高度加1
var isBalanced = function(root) { if(root==null) return true; // 判断空值 // 计算左右子树的最大高度差,并且要求左右子树也满足平衡性质 return Math.abs(height(root.left) - height(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right); };
var height = function(root){ if (root == null) return 0; // root==null 代表最深子结点,可以赋值 // 获取子结点最大值加一,返回当前结点高度 return Math.max(height(root.left), height(root.right)) + 1; }
自下至上的递归
由于如果自上至下的递归,那么如果p结点的高度是d,那么height(p)最多会被调用d次,因为每次遍历到它的任意一个祖先结点,它都会被调用。这样就会出现不必要的重复计算,大大增加了时间复杂度,所以,为了避免这种结果,我们最好的办法就是自下至上的调用。
自下至上则类似于后序遍历,对于当前遍历的结点,先递归调用其左右子树是否平衡,在判断以其结点为根的子树是否平衡,如果平衡,返回高度,否则直接返回false。只要存在一个子树不平衡,该二叉树不平衡。
var isBalanced = function(root) {
return height(root)>=0
};
var height = function(root){
// 终止判断,如果结点不存在,返回null
if(root ==null) return 0;
// 递归左右子树
let left = height(root.left)
let right = height(root.right)
// 判断左右子树是否平衡,当前树是否平衡
if(left==-1|| right==-1||Math.abs(left-right)>1){
return -1
}else{
// 都平衡的啥深度加1
return Math.max(left,right)+1
}
}
由于此下而上的判断,所以每个结点都只需要处理一次,最坏的情况下也只需要遍历二叉树中的所有结点,因此时间复杂度为O(n)