0110 Solved

This commit is contained in:
王子通 2020-04-29 11:46:01 +08:00
parent 2b328d0f6d
commit ea08607c2b
5 changed files with 165 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

View File

@ -0,0 +1,165 @@
# LeetCode 110 号问题平衡二叉树
> 本文首发于公众号图解面试算法 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一
>
> 同步博客https://www.algomooc.com
题目来源于 LeetCode 上第 110 号问题平衡二叉树
### 题目描述
给定一个二叉树判断它是否是高度平衡的二叉树
本题中一棵高度平衡二叉树定义为
> 一个二叉树*每个节点* 的左右两个子树的高度差的绝对值不超过1
**示例 1:**
```
3
/ \
9 20
/ \
15 7
```
返回 `true`
**示例 2:**
给定二叉树 `[1,2,2,3,3,null,null,4,4]`
```
1
/ \
2 2
/ \
3 3
/ \
4 4
```
返回 `false`
### 题目解析 - 自顶向下
这道题可以算是递归的充分使用了, 每一个子树都是子问题.
根据题意, 直观的想法就是计算当前节点左右子树的高度差了, 具体算法流程如下:
*定义* 方法 `depth(root)` 计算 root 最大高度
- **终止条件** `root` 为空即越过叶子节点则返回高度 0
- **返回值** Max(左子树高度, 右子树高度 ) + 1
*定义* 方法 `isBalanced(root)` 判断树 `root` 是否平衡
- **特例处理** 若树根节点 `root` 为空则直接返回 true
- **返回值** 所有子树都需要满足平衡树性质因此以下三者使用与 逻辑与 连接
- `abs(depth(root.left) - depth(root.right)) < 2` 判断 **当前子树** 是否是平衡树
- `isBalanced(root.left)` 先序遍历递归判断 **当前子树的左子树** 是否是平衡树
- `isBalanced(root.right)` 先序遍历递归判断 **当前子树的右子树** 是否是平衡树
> 通过流程能发现, 暴力法虽然容易想到, 但是会产生大量冗余计算, 因此时间复杂度也就会高;
>
> 想避免这种情况, 移步向下看 自底向上 方法
### 动画描述
<img src="../Animation/Animation1.gif" alt="Animation1" style="zoom:150%;" />
### 参考代码
```javascript
/**
* JavaScript 描述
* 自顶向下递归
*/
function depth(root) {
if (root == null) {
return 0;
}
return Math.max(depth(root.left), depth(root.right)) + 1;
};
var isBalanced = function(root) {
if (root == null) {
return true;
}
return Math.abs(depth(root.left) - depth(root.right)) < 2 &&
isBalanced(root.left) &&
isBalanced(root.right);
};
```
### 复杂度分析
- 时间复杂度: **O(Nlog_2 N)**
最差情况下 isBalanced(root) 遍历树所有节点占用 O(N)O(N) 判断每个节点的最大高度 depth(root) 需要遍历 各子树的所有节点 子树的节点数的复杂度为 O(log_2 N)
- 空间复杂度: **O(N)**
最差情况下树退化为链表时系统递归需要使用 O(N) 的栈空间
### 题目解析 - 自底向上
**自顶向下** 计算 `depth` 存在大量冗余, 每次调用 `depth` 要同时计算其子树高度
**自底向上** 计算每个子树的高度只会计算一次先递归计算当前节点的子节点高度然后再通过子节点高度判断当前节点是否平衡从而消除冗余
**自底向上** **自顶向下** 的逻辑相反首先判断子树是否平衡然后比较子树高度判断父节点是否平衡算法如下
*定义* 方法 `recur(root):` : 判断子树是否平衡 | 返回当前节点高度
- **递归终止条件**
- 当越过叶子节点时, 返回高度 0
- 当左子树高度 `left== -1` 代表此子树的 **子树** 不是平衡树, 因此直接返回 `-1`
- **递归返回值**
- 当节点 `root` / 右子树的高度差 < 2返回以节点 root 为根节点的子树的最大高度Max( left, right ) + 1
- 当节点 `root` / 右子树的高度差 >= 2 则返回 `-1` , 代表 **此子树不是平衡树**
*定义* 方法 `isBalanced(root)` : 判断当前树是否平衡
- **返回值** `recur(root) != 1` , 则说明此树平衡, 返回 `true` , 否则返回 `false`
### 动画描述
<img src="../Animation/Animation2.gif" alt="Animation2" style="zoom:150%;" />
### 参考代码
```javascript
/**
* JavaScript 描述
* 自底向上递归
*/
function recur(root) {
if (root == null) {
return 0;
}
let leftHeight = recur(root.left);
if (leftHeight == -1) {
return -1;
}
let rightHeight = recur(root.right);
if (rightHeight == -1) {
return -1;
}
return Math.abs(leftHeight - rightHeight) < 2 ?
Math.max(leftHeight,rightHeight) + 1 : -1;
};
var isBalanced = function(root) {
return recur(root) != -1;
};
```
### 复杂度分析
- 时间复杂度 **O(N)** N为树的节点数最差情况下需要递归遍历树的所有节点
- 空间复杂度 **O(N)** 最差情况下树退化为链表时系统递归需要使用 O(N) 的栈空间
![](../../Pictures/qrcode.jpg)