Skip to content

Commit 25db79c

Browse files
committed
Tree Traversal in Ruby
1 parent 7fe5bfe commit 25db79c

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

CONTRIBUTORS.md

+1
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,4 @@ This file lists everyone, who contributed to this repo and wanted to show up her
6161
- Hugo Salou
6262
- Dimitri Belopopsky
6363
+ Henrik Abel Christensen
64+
- Tarun Vellishetty
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
def create_tree(rows, children)
2+
return { id: rows, children: [] } if rows.zero?
3+
4+
{
5+
id: rows,
6+
children: Array.new(children, create_tree(rows - 1, children))
7+
}
8+
end
9+
10+
def dfs_preorder(tree)
11+
print "#{tree[:id]} "
12+
tree[:children].each { |child| dfs_preorder(child) }
13+
end
14+
15+
def dfs_postorder(tree)
16+
tree[:children].each { |child| dfs_postorder(child) }
17+
print "#{tree[:id]} "
18+
end
19+
20+
def dfs_inorder(tree)
21+
return unless tree
22+
23+
if tree[:children].count > 2
24+
raise 'Postorder traversal is only valid for binary trees'
25+
end
26+
27+
dfs_inorder(tree[:children][0])
28+
print "#{tree[:id]} "
29+
dfs_inorder(tree[:children][1])
30+
end
31+
32+
def dfs_iterative(tree)
33+
stack = [tree]
34+
while stack.count.positive?
35+
current = stack.pop
36+
print "#{current[:id]} "
37+
stack.push(*current[:children])
38+
end
39+
end
40+
41+
def bfs(tree)
42+
queue = [tree]
43+
while queue.count.positive?
44+
current = queue.shift
45+
print "#{current[:id]} "
46+
queue.push(*current[:children])
47+
end
48+
end
49+
50+
root = create_tree(2, 3)
51+
puts "[#]\nRecursive DFS:"
52+
dfs_preorder(root)
53+
puts ""
54+
puts "[#]\nRecursive Postorder DFS:"
55+
dfs_postorder(root)
56+
puts ""
57+
puts "[#]\nStack-based DFS:"
58+
dfs_iterative(root)
59+
puts ""
60+
puts "[#]\nQueue-based DFS:"
61+
bfs(root)
62+
puts ""
63+
root_binary = create_tree(3, 2);
64+
puts "[#]\nRecursive Inorder DFS for Binary Tree:"
65+
dfs_inorder(root_binary)

contents/tree_traversal/tree_traversal.md

+14
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ As a note, a `node` struct is not necessary in javascript, so this is an example
4646
[import:6-6, lang:"matlab"](code/matlab/tree.m)
4747
{% sample lang="coco" %}
4848
[import:3-3, lang:"coconut"](code/coconut/tree_traversal.coco)
49+
{% sample lang="ruby" %}
50+
[import:1-8, lang:"ruby"](code/ruby/tree.rb)
4951
{% endmethod %}
5052

5153
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:
@@ -93,6 +95,8 @@ Because of this, the most straightforward way to traverse the tree might be recu
9395
[import:31-45, lang:"matlab"](code/matlab/tree.m)
9496
{% sample lang="coco" %}
9597
[import:5-9, lang:"coconut"](code/coconut/tree_traversal.coco)
98+
{% sample lang="ruby" %}
99+
[import:10-13, lang:"ruby"](code/ruby/tree.rb)
96100
{% endmethod %}
97101

98102
At least to me, this makes a lot of sense. We fight recursion with recursion! First, we first output the node we are on and then we call `DFS_recursive(...)` on each of its children nodes. This method of tree traversal does what its name implies: it goes to the depths of the tree first before going through the rest of the branches. In this case, the ordering looks like:
@@ -149,6 +153,8 @@ Now, in this case the first element searched through is still the root of the tr
149153
[import:47-62, lang:"matlab"](code/matlab/tree.m)
150154
{% sample lang="coco" %}
151155
[import:11-15, lang:="coconut"](code/coconut/tree_traversal.coco)
156+
{% sample lang="ruby" %}
157+
[import:15-18, lang:"ruby"](code/ruby/tree.rb)
152158
{% endmethod %}
153159

154160
<p>
@@ -200,6 +206,8 @@ In this case, the first node visited is at the bottom of the tree and moves up t
200206
[import:64-82, lang:"matlab"](code/matlab/tree.m)
201207
{% sample lang="coco" %}
202208
[import:17-30, lang:"coconut"](code/coconut/tree_traversal.coco)
209+
{% sample lang="ruby" %}
210+
[import:20-30, lang:"ruby"](code/ruby/tree.rb)
203211
{% endmethod %}
204212

205213
<p>
@@ -260,6 +268,8 @@ In code, it looks like this:
260268
[import:84-106, lang:"matlab"](code/matlab/tree.m)
261269
{% sample lang="coco" %}
262270
[import:32-39, lang:"coconut"](code/coconut/tree_traversal.coco)
271+
{% sample lang="ruby" %}
272+
[import:32-39, lang:"ruby"](code/ruby/tree.rb)
263273
{% endmethod %}
264274

265275
All this said, there are a few details about DFS that might not be ideal, depending on the situation. For example, if we use DFS on 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:
@@ -313,6 +323,8 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can
313323
[import:108-129, lang:"matlab"](code/matlab/tree.m)
314324
{% sample lang="coco" %}
315325
[import:41-48, lang:"coconut"](code/coconut/tree_traversal.coco)
326+
{% sample lang="ruby" %}
327+
[import:41-48, lang:"ruby"](code/ruby/tree.rb)
316328
{% endmethod %}
317329

318330
## Video Explanation
@@ -377,6 +389,8 @@ The code snippets were taken from this [Scratch project](https://scratch.mit.edu
377389
[import, lang:"matlab"](code/matlab/tree.m)
378390
{% sample lang="coco" %}
379391
[import, lang:"coconut"](code/coconut/tree_traversal.coco)
392+
{% sample lang="ruby" %}
393+
[import, lang:"ruby"](code/ruby/tree.rb)
380394
{% endmethod %}
381395

382396

0 commit comments

Comments
 (0)