平衡二叉树
LeetCode传送门110. 平衡二叉树
题目
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as:
a binary tree in which the left and right subtrees of every node differ in height by no more than 1.
Example:
Input: root = [3,9,20,null,null,15,7]
Output: true
Input: root = []
Output: true
Constraints:
The number of nodes in the tree is in the range [0, 5000].
思考线
解题思路
看到本题,我的第一思路是要递归找出左右子树的高度,然后计算是否高度差小于等于1。再递归遍历计算其高度差是否小于等于1即可。
基于上面的想法我们首先要构建一个方法来计算二叉树的深度。最容易实现的就是写一个递归函数。
递归的终止条件为
- 当前节点为空,则返回0
- 当前节点节点没有左右子节点,则返回1
递归过程为
- 递归计算自己的左右子节点的长度。
返回结果为
- 左右子节点的最长长度 +1,为当前节点的长度。
代码如下
function findDeepth(root) {
if(!root) return 0;
if(!root.left && !root.right) return 1;
return Math.max(findDeepth(root.left), findDeepth(root.right)) +1;
}
有了计算二叉树高度的函数我们要找的最终解也呼之欲出了。
在isBalanced函数中只要
- 计算出左右子节点的高度,判断是否平衡,若不平衡则返回
false - 若平衡,则递归其左右子节点是否为平衡二叉树,若全都判定为平衡,则返回
true否则为false
那么最终代码如下
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isBalanced = function(root) {
if(!root) return true;
const left = findDeepth(root.left)
const right = findDeepth(root.right)
return Math.abs(left - right) <=1 && isBalanced(root.left) && isBalanced(root.right);
};
function findDeepth(root) {
if(!root) return 0;
if(!root.left && !root.right) return 1;
return Math.max(findDeepth(root.left), findDeepth(root.right)) +1;
}
对与这种解法的时间复杂度是多少呢?
复杂度分析
时间复杂度:O(n^2),其中 nn 是二叉树中的节点个数。 最坏情况下,二叉树是满二叉树,需要遍历二叉树中的所有节点,时间复杂度是 O(n)。 对于节点 p,如果它的高度是 d,则 findDeepth(p) 最多会被调用 d 次(即遍历到它的每一个祖先节点时)。对于平均的情况,一棵树的高度 hh 满足 O(h)=O(logn),因为 d ≤h,所以总时间复杂度为 O(nlogn)。对于最坏的情况,二叉树形成链式结构,高度为O(n),此时总时间复杂度为 O(n^2)。
空间复杂度:O(n),其中 n 是二叉树中的节点个数。空间复杂度主要取决于递归调用的层数,递归调用的层数不会超过 nn。
方法二
上面靠自觉做题的方法是自顶向下递归,因此对于同一个节点,计算数深度的函数findDeepth会被重复调用,导致时间复杂度较高。如果我们使用后序遍历的方法,先判断左右子节点是否平衡,则对于每个节点findDeepth只会被调用一次。
整体思路如下
- 后序遍历要判断的二叉树
- 看其左子节点是否为平衡二叉树,若平衡则再看右节点是否为平衡二叉树。若都是平衡的则当前节点也是平衡的,否则不平衡。
- 对于不平衡的节点我们直接返回
-1,对于平衡的节点我们返回对应的深度deepth >=0 - 我们只需要判断最后的
findDeepth返回的结果是否是-1即可。
代码如下
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isBalanced = function(root) {
return findDeepth2(root) !== -1;
};
function findDeepth2(root) {
if(!root) return 0;
if(!root.left && !root.right) return 1;
const left = findDeepth2(root.left);
if(left ===-1) return -1;
const right = findDeepth2(root.right);
if(right === -1) return -1;
if(Math.abs(left - right) <=1) return Math.max(left, right) +1;
return -1;
}
这就是我对本题的解法,如果有疑问或者更好的解答方式,欢迎留言互动。