Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d25bc3c

Browse files
Merge pull request youngyangyang04#2808 from gazeldx/set_iteration_binary_tree
二叉树的统一迭代法.md 加入"boolean标记法"和相关C++、Python代码实现
2 parents d2f6458 + 6c2e5a0 commit d25bc3c

File tree

1 file changed

+141
-6
lines changed

1 file changed

+141
-6
lines changed

‎problems/二叉树的统一迭代法.md‎

Lines changed: 141 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,16 @@
2727

2828
**那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。**
2929

30-
如何标记呢,**就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。** 这种方法也可以叫做标记法。
30+
如何标记呢?
31+
32+
* 方法一:**就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。** 这种方法可以叫做`空指针标记法`
33+
34+
* 方法二:**加一个 `boolean` 值跟随每个节点,`false` (默认值) 表示需要为该节点和它的左右儿子安排在栈中的位次,`true` 表示该节点的位次之前已经安排过了,可以收割节点了。**
35+
这种方法可以叫做`boolean 标记法`,样例代码见下文`C++ 和 Python 的 boolean 标记法`。 这种方法更容易理解,在面试中更容易写出来。
3136

3237
### 迭代法中序遍历
3338

34-
中序遍历代码如下:(详细注释)
39+
> 中序遍历(空指针标记法)代码如下:(详细注释)
3540
3641
```CPP
3742
class Solution {
@@ -70,6 +75,45 @@ public:
7075

7176
可以看出我们将访问的节点直接加入到栈中,但如果是处理的节点则后面放入一个空节点, 这样只有空节点弹出的时候,才将下一个节点放进结果集。
7277

78+
> 中序遍历(boolean 标记法):
79+
```c++
80+
class Solution {
81+
public:
82+
vector<int> inorderTraversal(TreeNode* root) {
83+
vector<int> result;
84+
stack<pair<TreeNode*, bool>> st;
85+
if (root != nullptr)
86+
st.push(make_pair(root, false)); // 多加一个参数,false 为默认值,含义见下文注释
87+
88+
while (!st.empty()) {
89+
auto node = st.top().first;
90+
auto visited = st.top().second; //多加一个 visited 参数,使"迭代统一写法"成为一件简单的事
91+
st.pop();
92+
93+
if (visited) { // visited 为 True,表示该节点和两个儿子位次之前已经安排过了,现在可以收割节点了
94+
result.push_back(node->val);
95+
continue;
96+
}
97+
98+
// visited 当前为 false, 表示初次访问本节点,此次访问的目的是"把自己和两个儿子在栈中安排好位次"。
99+
100+
// 中序遍历是'左中右',右儿子最先入栈,最后出栈。
101+
if (node->right)
102+
st.push(make_pair(node->right, false));
103+
104+
// 把自己加回到栈中,位置居中。
105+
// 同时,设置 visited 为 true,表示下次再访问本节点时,允许收割。
106+
st.push(make_pair(node, true));
107+
108+
if (node->left)
109+
st.push(make_pair(node->left, false)); // 左儿子最后入栈,最先出栈
110+
}
111+
112+
return result;
113+
}
114+
};
115+
```
116+
73117
此时我们再来看前序遍历代码。
74118

75119
### 迭代法前序遍历
@@ -105,7 +149,7 @@ public:
105149
106150
### 迭代法后序遍历
107151
108-
后续遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**)
152+
> 后续遍历代码如下: (**注意此时我们和中序遍历相比仅仅改变了两行代码的顺序**)
109153
110154
```CPP
111155
class Solution {
@@ -136,6 +180,42 @@ public:
136180
};
137181
```
138182

183+
> 迭代法后序遍历(boolean 标记法):
184+
```c++
185+
class Solution {
186+
public:
187+
vector<int> postorderTraversal(TreeNode* root) {
188+
vector<int> result;
189+
stack<pair<TreeNode*, bool>> st;
190+
if (root != nullptr)
191+
st.push(make_pair(root, false)); // 多加一个参数,false 为默认值,含义见下文
192+
193+
while (!st.empty()) {
194+
auto node = st.top().first;
195+
auto visited = st.top().second; //多加一个 visited 参数,使"迭代统一写法"成为一件简单的事
196+
st.pop();
197+
198+
if (visited) { // visited 为 True,表示该节点和两个儿子位次之前已经安排过了,现在可以收割节点了
199+
result.push_back(node->val);
200+
continue;
201+
}
202+
203+
// visited 当前为 false, 表示初次访问本节点,此次访问的目的是"把自己和两个儿子在栈中安排好位次"。
204+
// 后序遍历是'左右中',节点自己最先入栈,最后出栈。
205+
// 同时,设置 visited 为 true,表示下次再访问本节点时,允许收割。
206+
st.push(make_pair(node, true));
207+
208+
if (node->right)
209+
st.push(make_pair(node->right, false)); // 右儿子位置居中
210+
211+
if (node->left)
212+
st.push(make_pair(node->left, false)); // 左儿子最后入栈,最先出栈
213+
}
214+
215+
return result;
216+
}
217+
};
218+
```
139219
## 总结
140220

141221
此时我们写出了统一风格的迭代法,不用在纠结于前序写出来了,中序写不出来的情况了。
@@ -234,7 +314,7 @@ class Solution {
234314

235315
### Python:
236316

237-
迭代法前序遍历:
317+
> 迭代法前序遍历(空指针标记法):
238318
```python
239319
class Solution:
240320
def preorderTraversal(self, root: TreeNode) -> List[int]:
@@ -257,7 +337,7 @@ class Solution:
257337
return result
258338
```
259339

260-
迭代法中序遍历:
340+
> 迭代法中序遍历(空指针标记法):
261341
```python
262342
class Solution:
263343
def inorderTraversal(self, root: TreeNode) -> List[int]:
@@ -282,7 +362,7 @@ class Solution:
282362
return result
283363
```
284364

285-
迭代法后序遍历:
365+
> 迭代法后序遍历(空指针标记法):
286366
```python
287367
class Solution:
288368
def postorderTraversal(self, root: TreeNode) -> List[int]:
@@ -306,6 +386,61 @@ class Solution:
306386
return result
307387
```
308388

389+
> 中序遍历,统一迭代(boolean 标记法):
390+
```python
391+
class Solution:
392+
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
393+
values = []
394+
stack = [(root, False)] if root else [] # 多加一个参数,False 为默认值,含义见下文
395+
396+
while stack:
397+
node, visited = stack.pop() # 多加一个 visited 参数,使"迭代统一写法"成为一件简单的事
398+
399+
if visited: # visited 为 True,表示该节点和两个儿子的位次之前已经安排过了,现在可以收割节点了
400+
values.append(node.val)
401+
continue
402+
403+
# visited 当前为 False, 表示初次访问本节点,此次访问的目的是"把自己和两个儿子在栈中安排好位次"。
404+
# 中序遍历是'左中右',右儿子最先入栈,最后出栈。
405+
if node.right:
406+
stack.append((node.right, False))
407+
408+
stack.append((node, True)) # 把自己加回到栈中,位置居中。同时,设置 visited 为 True,表示下次再访问本节点时,允许收割
409+
410+
if node.left:
411+
stack.append((node.left, False)) # 左儿子最后入栈,最先出栈
412+
413+
return values
414+
```
415+
416+
> 后序遍历,统一迭代(boolean 标记法):
417+
```python
418+
class Solution:
419+
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
420+
values = []
421+
stack = [(root, False)] if root else [] # 多加一个参数,False 为默认值,含义见下文
422+
423+
while stack:
424+
node, visited = stack.pop() # 多加一个 visited 参数,使"迭代统一写法"成为一件简单的事
425+
426+
if visited: # visited 为 True,表示该节点和两个儿子位次之前已经安排过了,现在可以收割节点了
427+
values.append(node.val)
428+
continue
429+
430+
# visited 当前为 False, 表示初次访问本节点,此次访问的目的是"把自己和两个儿子在栈中安排好位次"
431+
# 后序遍历是'左右中',节点自己最先入栈,最后出栈。
432+
# 同时,设置 visited 为 True,表示下次再访问本节点时,允许收割。
433+
stack.append((node, True))
434+
435+
if node.right:
436+
stack.append((node.right, False)) # 右儿子位置居中
437+
438+
if node.left:
439+
stack.append((node.left, False)) # 左儿子最后入栈,最先出栈
440+
441+
return values
442+
```
443+
309444
### Go:
310445

311446
> 前序遍历统一迭代法

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /