Skip to content

Commit 61cce77

Browse files
committed
adding draft of tree traversal chapter.
1 parent d91d902 commit 61cce77

File tree

4 files changed

+190
-1
lines changed

4 files changed

+190
-1
lines changed
196 KB
Loading
Loading
Loading

chapters/fundamental_algorithms/tree_traversal.md

+190-1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,193 @@ $$
2323

2424
# Tree Traversal
2525

26-
COMING SOON!
26+
Trees are naturally recursive data structures, and because of this, we cannot access their elements like we might access the elements of a vector or array. Instead, we need to use more interesting methods to work through each element. This is often called *Tree Traversal*, and there are many different ways to do this. For now, we will restrict the discussion to two common and related methods of tree traversal: *Depth-First* and *Breadth-First Search*. First, let's take a look at the tree data structure:
27+
28+
![A simple tree](full_tree.png)
29+
30+
Note that trees vary greatly in shape and size depending on how they are used; however, they are composed primarily of nodes that house other, children nodes, like so:
31+
32+
```cpp
33+
struct node{
34+
std::vector<node> children;
35+
int ID;
36+
};
37+
```
38+
39+
Because of this, the most straightforward way to traverse the tree might be recursive. This naturally leads us to the Depth-First Search (DFS) method:
40+
41+
```cpp
42+
void DFS_recursive(const node& n){
43+
if (n.children.size() == 0){
44+
return;
45+
}
46+
47+
// Here we are doing something...
48+
std::cout << n.ID << '\n';
49+
for (int i = 0; i < n.children.size(); ++i){
50+
DFS_recursive(n.children[i]);
51+
}
52+
}
53+
54+
```
55+
56+
At least to me, this makes a lot of sense. We fight recursion with recursion! Rather surprisingly, though, we can traverse through our tree in the same order non-recursively by using a stack, which are data structures that hold multiple elements, but only allow you to interact with the very last element you put in. The idea here is simple:
57+
58+
1. Put the root node in the stack
59+
2. Take it out and put in its children
60+
3. Pop the top of the stack and put its children in
61+
4. Repeat 3 until the stack is empty
62+
63+
In code, it looks like this:
64+
65+
```cpp
66+
void DFS_stack(const node& n){
67+
std::stack<node> s;
68+
s.push(n);
69+
node temp;
70+
71+
while(s.size() > 0){
72+
std::cout << s.top().ID << '\n';
73+
temp = s.top();
74+
s.pop();
75+
for (int i = 0; i < temp.children.size(); ++i){
76+
s.push(temp.children[i]);
77+
}
78+
}
79+
}
80+
```
81+
82+
To be clear, depth-first search traverses through the nodes in a tree in the following order:
83+
84+
![DFS ordering](full_tree_DFS.png)
85+
86+
This means that if we have an incredibly long tree, we will spend a lot of time going further and further down a single branch without searching the rest of the data structure. In addition, it is not the natural way humans would order a tree if asked to number all the nodes from top to bottom. I would argue a more natural traversal order would look something like this:
87+
88+
![BFS ordering](full_tree_BFS.png)
89+
90+
And this is exactly what Breadth-First Search (BFS) does! On top of that, it can be implemented in the same way as the `DFS_stack(...)` function above, simply by swapping the `stack` for a `queue`, which is similar to a stack, exept that it only allows you to interact with the very first element instead of the last. In code, this looks something like:
91+
92+
```cpp
93+
void BFS_queue(const node& n){
94+
std::queue<node> q;
95+
q.push(n);
96+
node temp;
97+
98+
while(q.size() > 0){
99+
std::cout << q.front().ID << '\n';
100+
temp = q.front();
101+
q.pop();
102+
for (int i = 0; i < temp.children.size(); ++i){
103+
q.push(temp.children[i]);
104+
}
105+
}
106+
}
107+
108+
```
109+
110+
# Point of Discussion
111+
I have used C++ syntax for this chapter; however, this goes against my policy to keep the Algorithm Archive language-indifferent. On the one had, it's nice to see compilable code in the archive. On the other had, I don't want this to become a C++ book. I think I will try to come up with a clear psudocode scheme and use it throughout this book from now on, but I wanted to hear your thoughts.
112+
113+
Do you think we should be using real code snippets in the main text or stick them at the end?
114+
115+
# Example Code
116+
117+
```cpp
118+
/*-------------simple_tree_traversal.cpp--------------------------------------//
119+
*
120+
* Purpose: To implement basic tree traversal in C++.
121+
*
122+
* Notes: -Other languages will be implemented in the Arcane Algorithm Archive
123+
* -This screen will only be on the video for 1 second...
124+
* -... So, if you are reading this, great job! You know how to pause!
125+
* -There's no secret information here. It's just me rambling.
126+
* -Also: we are using a vector library here. Sorry!
127+
* -Oh yeah, compile with: g++ simple_tree_traversal.cpp
128+
*
129+
*-----------------------------------------------------------------------------*/
130+
131+
#include <iostream>
132+
#include <vector>
133+
#include <stack>
134+
#include <queue>
135+
136+
// So we heard you liked nodes...
137+
struct node{
138+
std::vector<node> children;
139+
int ID;
140+
};
141+
142+
// There are better ways to do this, I'm sure...
143+
void create_tree(node& n, int num_row, int num_child){
144+
// We'll just set the ID to whatever we want here...
145+
n.ID = num_row;
146+
if (num_row == 0){
147+
return;
148+
}
149+
150+
// Creating children
151+
n.children.reserve(num_child);
152+
for (int i = 0; i < num_child; ++i){
153+
node child;
154+
create_tree(child, num_row - 1, num_child);
155+
n.children.push_back(child);
156+
}
157+
158+
}
159+
160+
// Simple recursive scheme for DFS
161+
void DFS_recursive(const node& n){
162+
if (n.children.size() == 0){
163+
return;
164+
}
165+
166+
// Here we are doing something...
167+
std::cout << n.ID << '\n';
168+
for (int i = 0; i < n.children.size(); ++i){
169+
DFS_recursive(n.children[i]);
170+
}
171+
}
172+
173+
// Simple non-recursive scheme for DFS
174+
void DFS_stack(const node& n){
175+
std::stack<node> s;
176+
s.push(n);
177+
node temp;
178+
179+
while(s.size() > 0){
180+
std::cout << s.top().ID << '\n';
181+
temp = s.top();
182+
s.pop();
183+
for (int i = 0; i < temp.children.size(); ++i){
184+
s.push(temp.children[i]);
185+
}
186+
}
187+
}
188+
189+
// simple non-recursive scheme for BFS
190+
void BFS_queue(const node& n){
191+
std::queue<node> q;
192+
q.push(n);
193+
node temp;
194+
195+
while(q.size() > 0){
196+
std::cout << q.front().ID << '\n';
197+
temp = q.front();
198+
q.pop();
199+
for (int i = 0; i < temp.children.size(); ++i){
200+
q.push(temp.children[i]);
201+
}
202+
}
203+
}
204+
205+
int main(){
206+
207+
// Creating tree in main
208+
node root;
209+
create_tree(root, 3, 3);
210+
DFS_recursive(root);
211+
DFS_stack(root);
212+
BFS_queue(root);
213+
}
214+
215+
```

0 commit comments

Comments
 (0)