Skip to content

Commit ea08607

Browse files
committed
0110 Solved
1 parent 2b328d0 commit ea08607

File tree

5 files changed

+165
-0
lines changed

5 files changed

+165
-0
lines changed
Binary file not shown.
Binary file not shown.
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# LeetCode 第 110 号问题:平衡二叉树
2+
3+
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
4+
>
5+
> 同步博客:https://www.algomooc.com
6+
7+
题目来源于 LeetCode 上第 110 号问题:平衡二叉树。
8+
9+
### 题目描述
10+
11+
给定一个二叉树,判断它是否是高度平衡的二叉树。
12+
13+
本题中,一棵高度平衡二叉树定义为:
14+
15+
> 一个二叉树*每个节点* 的左右两个子树的高度差的绝对值不超过1。
16+
17+
**示例 1:**
18+
19+
```
20+
3
21+
/ \
22+
9 20
23+
/ \
24+
15 7
25+
```
26+
27+
返回 `true`
28+
29+
**示例 2:**
30+
31+
给定二叉树 `[1,2,2,3,3,null,null,4,4]`
32+
33+
```
34+
1
35+
/ \
36+
2 2
37+
/ \
38+
3 3
39+
/ \
40+
4 4
41+
```
42+
43+
返回 `false`
44+
45+
### 题目解析 - 自顶向下
46+
47+
这道题可以算是递归的充分使用了, 每一个子树都是子问题.
48+
49+
根据题意, 直观的想法就是计算当前节点左右子树的高度差了, 具体算法流程如下:
50+
51+
*定义* 方法 `depth(root)` 计算 root 最大高度
52+
53+
- **终止条件:**`root` 为空,即越过叶子节点,则返回高度 0
54+
- **返回值:** Max(左子树高度, 右子树高度 ) + 1
55+
56+
*定义* 方法 `isBalanced(root)` 判断树 `root` 是否平衡
57+
58+
- **特例处理:** 若树根节点 `root` 为空,则直接返回 true
59+
- **返回值:** 所有子树都需要满足平衡树性质,因此以下三者使用与 逻辑与 连接
60+
- `abs(depth(root.left) - depth(root.right)) < 2` :判断 **当前子树** 是否是平衡树
61+
- `isBalanced(root.left)` : 先序遍历递归,判断 **当前子树的左子树** 是否是平衡树;
62+
- `isBalanced(root.right)` : 先序遍历递归,判断 **当前子树的右子树** 是否是平衡树;
63+
64+
> 通过流程能发现, 暴力法虽然容易想到, 但是会产生大量冗余计算, 因此时间复杂度也就会高;
65+
>
66+
> 想避免这种情况, 移步向下看 自底向上 方法
67+
68+
### 动画描述
69+
70+
<img src="../Animation/Animation1.gif" alt="Animation1" style="zoom:150%;" />
71+
72+
### 参考代码
73+
74+
```javascript
75+
/**
76+
* JavaScript 描述
77+
* 自顶向下递归
78+
*/
79+
function depth(root) {
80+
if (root == null) {
81+
return 0;
82+
}
83+
return Math.max(depth(root.left), depth(root.right)) + 1;
84+
};
85+
var isBalanced = function(root) {
86+
if (root == null) {
87+
return true;
88+
}
89+
return Math.abs(depth(root.left) - depth(root.right)) < 2 &&
90+
isBalanced(root.left) &&
91+
isBalanced(root.right);
92+
};
93+
```
94+
95+
### 复杂度分析
96+
97+
- 时间复杂度: **O(Nlog_2 N)**
98+
99+
最差情况下, isBalanced(root) 遍历树所有节点,占用 O(N)O(N) ;判断每个节点的最大高度 depth(root) 需要遍历 各子树的所有节点 ,子树的节点数的复杂度为 O(log_2 N)
100+
101+
- 空间复杂度: **O(N)**
102+
103+
最差情况下(树退化为链表时),系统递归需要使用 O(N) 的栈空间
104+
105+
### 题目解析 - 自底向上
106+
107+
**自顶向下** 计算 `depth` 存在大量冗余, 每次调用 `depth` 时,要同时计算其子树高度。
108+
109+
**自底向上** 计算每个子树的高度只会计算一次。先递归计算当前节点的子节点高度,然后再通过子节点高度判断当前节点是否平衡,从而消除冗余。
110+
111+
**自底向上****自顶向下** 的逻辑相反,首先判断子树是否平衡,然后比较子树高度判断父节点是否平衡。算法如下:
112+
113+
*定义* 方法 `recur(root):` : 判断子树是否平衡 | 返回当前节点高度
114+
115+
- **递归终止条件:**
116+
- 当越过叶子节点时, 返回高度 0
117+
- 当左(右)子树高度 `left== -1` 时,代表此子树的 **左(右)子树** 不是平衡树, 因此直接返回 `-1`
118+
- **递归返回值:**
119+
- 当节点 `root` 左 / 右子树的高度差 < 2:返回以节点 root 为根节点的子树的最大高度Max( left, right ) + 1
120+
- 当节点 `root` 左 / 右子树的高度差 >= 2 :则返回 `-1` , 代表 **此子树不是平衡树**
121+
122+
*定义* 方法 `isBalanced(root)` : 判断当前树是否平衡
123+
124+
- **返回值:**`recur(root) != 1` , 则说明此树平衡, 返回 `true` , 否则返回 `false`
125+
126+
### 动画描述
127+
128+
<img src="../Animation/Animation2.gif" alt="Animation2" style="zoom:150%;" />
129+
130+
### 参考代码
131+
132+
```javascript
133+
/**
134+
* JavaScript 描述
135+
* 自底向上递归
136+
*/
137+
function recur(root) {
138+
if (root == null) {
139+
return 0;
140+
}
141+
let leftHeight = recur(root.left);
142+
if (leftHeight == -1) {
143+
return -1;
144+
}
145+
let rightHeight = recur(root.right);
146+
if (rightHeight == -1) {
147+
return -1;
148+
}
149+
return Math.abs(leftHeight - rightHeight) < 2 ?
150+
Math.max(leftHeight,rightHeight) + 1 : -1;
151+
};
152+
var isBalanced = function(root) {
153+
return recur(root) != -1;
154+
};
155+
```
156+
157+
### 复杂度分析
158+
159+
- 时间复杂度 **O(N)**: N为树的节点数;最差情况下,需要递归遍历树的所有节点。
160+
- 空间复杂度 **O(N)**: 最差情况下(树退化为链表时),系统递归需要使用 O(N) 的栈空间。
161+
162+
163+
164+
165+
![](../../Pictures/qrcode.jpg)

0 commit comments

Comments
 (0)