Skip to content

Commit 5c52e99

Browse files
committed
update: 二叉树遍历系列
1 parent 99eb7f0 commit 5c52e99

File tree

2 files changed

+162
-91
lines changed

2 files changed

+162
-91
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,13 @@
162162

163163
### 高频面试题
164164

165+
- [x] [【day-34】二叉树遍历系列](./medium/hot/34.traversal-of-binary-tree.md)
165166
- [x] [【day-34】581.最短无序连续子数组](./medium/day-34.md)
166167
- [x] [【day-35】78.子集](./medium/day-35.md)
167168
- [x] [【day-36】62.不同路径](./medium/day-36.md)
168169
- [x] [【day-37】有效括号系列](./medium/day-37.md)
169170
- [x] [【day-38】反转链表系列](./medium/day-38.md)
170171
- [x] [【day-39】前缀和系列](./medium/day-39.md)
171-
- [x] [【day-40】二叉树遍历系列](./medium/day-40.md)
172172

173173
### 前缀树
174174

Lines changed: 161 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
**扩展**
1212

13-
- 多叉树的遍历,参考[图的遍历](https://github.com/suukii/Articles/blob/master/articles/dsa_graph.md)
14-
- O(1)空间的遍历,[Morris Traversal](https://github.com/suukii/Articles/blob/master/articles/dsa_binary_tree.md#morris-traversal)
13+
- 多叉树的遍历,参考[图的遍历](https://github.com/suukii/Articles/blob/master/articles/dsa/dsa_graph.md#%E5%9B%BE%E7%9A%84%E9%81%8D%E5%8E%86)
14+
- $O(1)$ 空间的遍历,[Morris Traversal](https://github.com/suukii/Articles/blob/master/articles/dsa/dsa_binary_tree.md#morris-traversal)
1515

1616
# 144.二叉树的前序遍历
1717

@@ -33,8 +33,8 @@ preorder(root.right)
3333

3434
### 复杂度分析
3535

36-
- 时间复杂度:O(2^h),h 为二叉树的高度。也可以表示为 O(n),n 为二叉树的节点数。
37-
- 空间复杂度:O(h),h 为二叉树的高度。
36+
- 时间复杂度:$O(2^h)$,h 为二叉树的高度。也可以表示为 $O(n)$,n 为二叉树的节点数。
37+
- 空间复杂度:$O(h)$,h 为二叉树的高度。
3838

3939
### 代码
4040

@@ -53,13 +53,13 @@ JavaScript Code
5353
* @return {number[]}
5454
*/
5555
var preorderTraversal = function (root, res = []) {
56-
if (!root) return []
56+
if (!root) return [];
5757

58-
res.push(root.val)
59-
root.left && preorderTraversal(root.left, res)
60-
root.right && preorderTraversal(root.right, res)
61-
return res
62-
}
58+
res.push(root.val);
59+
root.left && preorderTraversal(root.left, res);
60+
root.right && preorderTraversal(root.right, res);
61+
return res;
62+
};
6363
```
6464

6565
## 迭代
@@ -87,18 +87,18 @@ JavaScript Code
8787
* @return {number[]}
8888
*/
8989
var preorderTraversal = function (root) {
90-
const stack = [root]
91-
const res = []
92-
93-
while (stack.length > 0) {
94-
const node = stack.pop()
95-
if (node) {
96-
res.push(node.val)
97-
stack.push(node.right, node.left)
90+
const stack = [root];
91+
const res = [];
92+
93+
while (stack.length > 0) {
94+
const node = stack.pop();
95+
if (node) {
96+
res.push(node.val);
97+
stack.push(node.right, node.left);
98+
}
9899
}
99-
}
100-
return res
101-
}
100+
return res;
101+
};
102102
```
103103

104104
# 94.二叉树的中序遍历
@@ -136,12 +136,12 @@ JavaScript Code
136136
* @return {number[]}
137137
*/
138138
var inorderTraversal = function (root, res = []) {
139-
if (!root) return []
140-
root.left && inorderTraversal(root.left, res)
141-
res.push(root.val)
142-
root.right && inorderTraversal(root.right, res)
143-
return res
144-
}
139+
if (!root) return [];
140+
root.left && inorderTraversal(root.left, res);
141+
res.push(root.val);
142+
root.right && inorderTraversal(root.right, res);
143+
return res;
144+
};
145145
```
146146

147147
## 迭代
@@ -170,20 +170,20 @@ JavaScript Code
170170
* @return {number[]}
171171
*/
172172
var inorderTraversal = function (root) {
173-
const stack = []
174-
const res = []
175-
176-
while (root || stack.length > 0) {
177-
while (root) {
178-
stack.push(root)
179-
root = root.left
173+
const stack = [];
174+
const res = [];
175+
176+
while (root || stack.length > 0) {
177+
while (root) {
178+
stack.push(root);
179+
root = root.left;
180+
}
181+
root = stack.pop();
182+
res.push(root.val);
183+
root = root.right;
180184
}
181-
root = stack.pop()
182-
res.push(root.val)
183-
root = root.right
184-
}
185-
return res
186-
}
185+
return res;
186+
};
187187
```
188188

189189
# 145.二叉树的后序遍历
@@ -221,24 +221,28 @@ JavaScript Code
221221
* @return {number[]}
222222
*/
223223
var postorderTraversal = function (root, res = []) {
224-
if (!root) return []
225-
root.left && postorderTraversal(root.left, res)
226-
root.right && postorderTraversal(root.right, res)
227-
res.push(root.val)
228-
return res
229-
}
224+
if (!root) return [];
225+
226+
root.left && postorderTraversal(root.left, res);
227+
root.right && postorderTraversal(root.right, res);
228+
res.push(root.val);
229+
230+
return res;
231+
};
230232
```
231233

232234
## 迭代
233235

234-
### 思路
236+
### 思路 1
237+
238+
这是比较讨巧的思路。
235239

236240
1. 二叉树的前序遍历是 root -> left -> right
237241
2. 二叉树的后序遍历是 left -> right -> root
238242
3. 可以看到后序遍历差不多是前序遍历的结果倒转过来,我们可以把前序遍历的套路拿过来稍做改动。
239243
4. 只需把第 2 步把 node 存入 res 这一步由 `res.push(node.val)` 改为 `res.unshift(node.val)`,并且将左右子节点入栈的顺序调换一下即可。
240244

241-
### 代码
245+
### 代码 1
242246

243247
JavaScript Code
244248

@@ -255,17 +259,79 @@ JavaScript Code
255259
* @return {number[]}
256260
*/
257261
var postorderTraversal = function (root) {
258-
const stack = [root]
259-
const res = []
260-
while (stack.length > 0) {
261-
const node = stack.pop()
262-
if (node) {
263-
stack.push(node.left, node.right)
264-
res.unshift(node.val)
262+
const stack = [root];
263+
const res = [];
264+
while (stack.length > 0) {
265+
const node = stack.pop();
266+
if (node) {
267+
stack.push(node.left, node.right);
268+
res.unshift(node.val);
269+
}
265270
}
266-
}
267-
return res
268-
}
271+
return res;
272+
};
273+
```
274+
275+
### 思路 2
276+
277+
这是比较正经的思路。
278+
279+
因为后序遍历的顺序是 left -> right -> root,我们需要先遍历 left,但如果直接遍历,访问完 left 之后没办法回到 root 了,因为二叉树的子节点是没有指向父节点的指针的。
280+
281+
所以,在遍历 left 之前,我们需要把 root 和 right 存起来,访问完 left 之后再回到 root,这时候需要判断,如果 root.right 存在且还没访问过,就先去访问 root.right,否则直接打印 root 的值。
282+
283+
1. 将 root.right 和 root 入栈,稍后遍历。
284+
2.`root = root.left` (相当于将 root 看成一个 cur 指针)。
285+
3. 当 root 不为空时,重复步骤 1 和 2。
286+
4. 开始出栈,`root = stack.pop()`,检查 root.right 有没有被访问过:
287+
1. 如果没有被访问过的话,这时候 root.right 应该在栈顶,将 root.right 弹出,然后将 root 压回栈等最后再遍历。接着让 `root = root.right`,开始处理右子树。
288+
2. 如果已经被访问过,或者 root.right 为空的话,这时我们直接打印 root 的值就好了。
289+
290+
### 代码 2
291+
292+
JavaScript Code
293+
294+
```js
295+
/**
296+
* Definition for a binary tree node.
297+
* function TreeNode(val) {
298+
* this.val = val;
299+
* this.left = this.right = null;
300+
* }
301+
*/
302+
/**
303+
* @param {TreeNode} root
304+
* @return {number[]}
305+
*/
306+
var postorderTraversal = function (root) {
307+
if (!root) return [];
308+
309+
const stack = [];
310+
const res = [];
311+
312+
while (true) {
313+
while (root) {
314+
root.right && stack.push(root.right);
315+
stack.push(root);
316+
root = root.left;
317+
}
318+
319+
root = stack.pop();
320+
321+
if (root.right && root.right === stack[stack.length - 1]) {
322+
const right = stack.pop();
323+
stack.push(root);
324+
root = right;
325+
} else {
326+
res.push(root.val);
327+
root = null;
328+
}
329+
330+
if (!stack.length) break;
331+
}
332+
333+
return res;
334+
};
269335
```
270336

271337
# 102.二叉树的层序遍历
@@ -288,8 +354,8 @@ var postorderTraversal = function (root) {
288354

289355
### 复杂度分析
290356

291-
- 时间复杂度:O(n),n 为树的节点数。
292-
- 空间复杂度:O(2^h),h 为树的高度,队列的最大长度是最深一层叶子节点的总数 2^(h-1)。
357+
- 时间复杂度:$O(n)$,n 为树的节点数。
358+
- 空间复杂度:$O(2^h)$,h 为树的高度,队列的最大长度是最深一层叶子节点的总数 $2^(h-1)$
293359

294360
### 代码
295361

@@ -309,27 +375,27 @@ JavaScript Code
309375
* @return {number[][]}
310376
*/
311377
var levelOrder = function (root) {
312-
if (!root) return []
313-
314-
const result = []
315-
const queue = [root, null]
316-
let level = 0
317-
318-
while (queue.length > 0) {
319-
const node = queue.shift()
320-
321-
if (node) {
322-
result[level] || (result[level] = [])
323-
result[level].push(node.val)
324-
node.left && queue.push(node.left)
325-
node.right && queue.push(node.right)
326-
} else {
327-
queue.length > 0 && queue.push(null)
328-
level++
378+
if (!root) return [];
379+
380+
const result = [];
381+
const queue = [root, null];
382+
let level = 0;
383+
384+
while (queue.length > 0) {
385+
const node = queue.shift();
386+
387+
if (node) {
388+
result[level] || (result[level] = []);
389+
result[level].push(node.val);
390+
node.left && queue.push(node.left);
391+
node.right && queue.push(node.right);
392+
} else {
393+
queue.length > 0 && queue.push(null);
394+
level++;
395+
}
329396
}
330-
}
331-
return result
332-
}
397+
return result;
398+
};
333399
```
334400

335401
Python Code
@@ -356,12 +422,15 @@ class Solution(object):
356422
while len(queue) > 0:
357423
size = len(queue)
358424
level = []
425+
359426
for i in range(size):
360427
node = queue.pop(0)
428+
361429
if node != None:
362430
level.append(node.val)
363431
if node.left != None: queue.append(node.left)
364432
if node.right != None: queue.append(node.right)
433+
365434
items.append(level)
366435
return items
367436
```
@@ -374,8 +443,8 @@ class Solution(object):
374443

375444
### 复杂度分析
376445

377-
- 时间复杂度:O(n), n 为二叉树的节点数。
378-
- 空间复杂度:O(n), n 为二叉树的节点数,`res` 数组的空间是 O(n),调用栈的空间最大是 O(h),h 是二叉树的深度,因为 n >= h,所以最后还是 O(n)。
446+
- 时间复杂度:$O(n)$, n 为二叉树的节点数。
447+
- 空间复杂度:$O(n)$, n 为二叉树的节点数,`res` 数组的空间是 $O(n)$,调用栈的空间最大是 $O(h)$,h 是二叉树的深度,因为 $n >= h$,所以最后还是 $O(n)$
379448

380449
### 代码
381450

@@ -394,12 +463,14 @@ JavaScript Code
394463
* @return {number[][]}
395464
*/
396465
var levelOrder = function (root, depth = 0, res = []) {
397-
if (!root) return []
398-
399-
res[depth] || (res[depth] = [])
400-
res[depth].push(root.val)
401-
levelOrder(root.left, depth + 1, res)
402-
levelOrder(root.right, depth + 1, res)
403-
return res
404-
}
466+
if (!root) return [];
467+
468+
res[depth] || (res[depth] = []);
469+
res[depth].push(root.val);
470+
471+
levelOrder(root.left, depth + 1, res);
472+
levelOrder(root.right, depth + 1, res);
473+
474+
return res;
475+
};
405476
```

0 commit comments

Comments
 (0)