Skip to content

Commit 830c2f7

Browse files
authored
feat(translation): binary-tree-traversal (azl397985856#362)
1 parent 11205f4 commit 830c2f7

File tree

2 files changed

+194
-1
lines changed

2 files changed

+194
-1
lines changed

README.en.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ The data structures mainly include:
248248

249249
- [Data Structure](./thinkings/basic-data-structure-en.md) (Drafts)
250250
- [Basic Algorithm](./thinkings/basic-algorithm-en.md)(Drafts)
251-
- [Binary Tree Traversal](./thinkings/binary-tree-traversal-en.md)
251+
- [Binary Tree Traversal](./thinkings/binary-tree-traversal.en.md)
252252
- [Dynamic Programming](./thinkings/dynamic-programming-en.md)
253253
- [Huffman Encode and Run Length Encode](./thinkings/run-length-encode-and-huffman-encode-en.md)
254254
- [Bloom Filter](./thinkings/bloom-filter-en.md)

thinkings/binary-tree-traversal.en.md

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Binary Tree Traversal
2+
3+
## Overview
4+
5+
Binary tree as a basic data structure and traversal as a fundamental algorithm, their combination leads to a lot of classic problems. This patern is often seen in many problems, either directly or indirectly.
6+
7+
> If you have grasped the traversal of binary trees, other complicated trees will probably be easy for you.
8+
9+
Following are the generally used ways for traversing trees.
10+
11+
- Depth First Traversals (DFS): Inorder, Preorder, Postorder
12+
13+
- Breadth First or Level Order Traversal (BFS)
14+
15+
There are applications for both DFS and BFS. Check out leetcode problem No.301 and No.609.
16+
17+
Stack can be used to simplify the process of DFS traversal. Besides, since tree is a recursive data structure, recursion and stack are two key points for DFS.
18+
19+
Graph for DFS:
20+
21+
![binary-tree-traversal-dfs](../assets/thinkings/binary-tree-traversal-dfs.gif)
22+
23+
(from https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/tree/depth-first-search)
24+
25+
The key point of BFS is how to decide whether the traversal of each level is done. The answer is using a variable as a flag to represent the end of the traversal of current level.
26+
27+
Let's dive into details.
28+
29+
## Preorder Traversal
30+
31+
related problem[144.binary-tree-preorder-traversal](../problems/144.binary-tree-preorder-traversal.md)
32+
33+
The traversal order of preorder traversal is `root-left-right`.
34+
35+
Algorithm Preorder
36+
37+
1. Visit the root node and push it into a stack.
38+
39+
2. Pop a node from the stack, and push its right and left child node into the stack respectively.
40+
41+
3. Repeat step 2.
42+
43+
Conclusion: This problem involves the clasic recursive data structure (i.e. a binary tree), and the algorithm above demonstrates how a simplified solution can be reached by using a stack.
44+
45+
If you look at the bigger picture, you'll find that the process of traversal is as followed. `Visit the left subtrees repectively from top to bottom, and visit the right subtrees repectively from bottom to top`. If we are to implement it from this perspective, things will be somewhat different. For the `top to bottom` part we can simply use recursion, and for the `bottom to top` part we can turn to stack.
46+
47+
The traversal will look something like this.
48+
49+
![binary-tree-traversal-preorder](../assets/thinkings/binary-tree-traversal-preorder.png)
50+
51+
This way of problem solving is a bit similar to `backtrack`, on which I have written a post. You can benefit a lot from it because it can be used to `solve all three DFS traversal problems` mentioned aboved. If you don't know this yet, make a memo on it.
52+
53+
## Inorder Traversal
54+
55+
related problem[94.binary-tree-inorder-traversal](../problems/94.binary-tree-inorder-traversal.md)
56+
57+
The traversal order of inorder traversal is `left-root-right`.
58+
59+
So the root node is not printed first. Things are getting a bit complicated here.
60+
61+
Algorithm Inorder
62+
63+
1. Visit the root and push it into a stack.
64+
65+
2. If there is a left child node, push it into the stack. Repeat this process until a leaf node reached.
66+
67+
> At this point the root node and all the left nodes are in the stack.
68+
69+
3. Start popping nodes from the stack. If a node has a right child node, push the child node into the stack. Repeat step 2.
70+
71+
It's worth pointing out that the inorder traversal of a binary search tree (BST) is a sorted array, which is helpful for coming up simplified solutions for some problems. e.g. [230.kth-smallest-element-in-a-bst](../problems/230.kth-smallest-element-in-a-bst.md) and [98.validate-binary-search-tree](../problems/98.validate-binary-search-tree.md)
72+
73+
## Postorder Traversal
74+
75+
related problem[145.binary-tree-postorder-traversal](../problems/145.binary-tree-postorder-traversal.md)
76+
77+
The traversal order of postorder traversal is `left-right-root`.
78+
79+
This one is a bit of a challange. It deserves the `hard` tag of leetcode.
80+
81+
In this case, the root node is printed not as the first but the last one. A cunning way to do it is to:
82+
83+
Record whether the current node has been visited. If 1) it's a leaf node or 2) both its left and right subtrees have been traversed, then it can be popped from the stack.
84+
85+
As for `1) it's a leaf node`, you can easily tell whether a node is a leaf if both its left and right are `null`.
86+
87+
As for `2) both its left and right subtrees have been traversed`, we only need a variable to record whether a node has been visited or not. In the worst case, we need to record the status for every single node and the space complexity is O(n). But if you come to think about it, as we are using a stack and start printing the result from the leaf nodes, it makes sense that we only record the status for the current node popping from the stack, reducing the space complexity to O(1). Please click the link above for more details.
88+
89+
## Level Order Traversal
90+
91+
The key point of level order traversal is how do we know whether the traversal of each level is done. The answer is that we use a variable as a flag representing the end of the traversal of the current level.
92+
93+
![binary-tree-traversal-bfs](../assets/thinkings/binary-tree-traversal-bfs.gif)
94+
95+
(from https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/tree/breadth-first-search)
96+
97+
Algorithm Level Order
98+
99+
1. Visit the root node, put it in a FIFO queue, put in the queue a special flag (we are using `null` here).
100+
101+
2. Dequeue a node.
102+
103+
3. If the node equals `null`, it means that all nodes of the current level have been visited. If the queue is empty, we do nothing. Or else we put in another `null`.
104+
105+
4. If the node is not `null`, meaning the traversal of current level has not finished yet, we enqueue its left subtree and right subtree repectively.
106+
107+
related problem[102.binary-tree-level-order-traversal](../problems/102.binary-tree-level-order-traversal.md)
108+
109+
## Bi-color marking
110+
111+
We know that there is a tri-color marking in garbage collection algorithm, which works as described below.
112+
113+
- The white color represents "not visited".
114+
115+
- The gray color represents "not all child nodes visited".
116+
117+
- The black color represents "all child nodes visited".
118+
119+
Enlightened by tri-color marking, a bi-color marking method can be invented to solve all three traversal problems with one solution.
120+
121+
The core idea is as followed.
122+
123+
- Use a color to mark whether a node has been visited or not. Nodes yet to be visited are marked as white and visited nodes are marked as gray.
124+
125+
- If we are visiting a white node, turn it into gray, and push it's right child node, itself, and it's left child node into the stack respectively.
126+
127+
- If we are visiting a gray node, print it.
128+
129+
Implementing inorder traversal with tri-color marking:
130+
131+
```python
132+
class Solution:
133+
def inorderTraversal(self, root: TreeNode) -> List[int]:
134+
WHITE, GRAY = 0, 1
135+
res = []
136+
stack = [(WHITE, root)]
137+
while stack:
138+
color, node = stack.pop()
139+
if node is None: continue
140+
if color == WHITE:
141+
stack.append((WHITE, node.right))
142+
stack.append((GRAY, node))
143+
stack.append((WHITE, node.left))
144+
else:
145+
res.append(node.val)
146+
return res
147+
```
148+
149+
Implementation of preorder and postorder traversal algorithms can be easily done by changing the order of pushing the child nodes into the stack.
150+
151+
## Morris Traversal
152+
153+
We can also use a method called Morris traversal, which involves no recursion or stack, and the time complexity is O(1).
154+
155+
```python
156+
def MorrisTraversal(root):
157+
curr = root
158+
159+
while curr:
160+
# If left child is null, print the
161+
# current node data. And, update
162+
# the current pointer to right child.
163+
if curr.left is None:
164+
print(curr.data, end= " ")
165+
curr = curr.right
166+
167+
else:
168+
# Find the inorder predecessor
169+
prev = curr.left
170+
171+
while prev.right is not None and prev.right is not curr:
172+
prev = prev.right
173+
174+
# If the right child of inorder
175+
# predecessor already points to
176+
# the current node, update the
177+
# current with it's right child
178+
if prev.right is curr:
179+
prev.right = None
180+
curr = curr.right
181+
182+
# else If right child doesn't point
183+
# to the current node, then print this
184+
# node's data and update the right child
185+
# pointer with the current node and update
186+
# the current with it's left child
187+
else:
188+
print (curr.data, end=" ")
189+
prev.right = curr
190+
curr = curr.left
191+
```
192+
193+
Reference: [what-is-morris-traversal](https://www.educative.io/edpresso/what-is-morris-traversal)

0 commit comments

Comments
 (0)