From 25db79c14eb194d06b8ab8ca386c4aa60e27518c Mon Sep 17 00:00:00 2001 From: Tarun Date: Sun, 13 Oct 2019 09:49:13 +0530 Subject: [PATCH] Tree Traversal in Ruby --- CONTRIBUTORS.md | 1 + contents/tree_traversal/code/ruby/tree.rb | 65 +++++++++++++++++++++++ contents/tree_traversal/tree_traversal.md | 14 +++++ 3 files changed, 80 insertions(+) create mode 100644 contents/tree_traversal/code/ruby/tree.rb diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 82f3c173a..54b41eacd 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -61,3 +61,4 @@ This file lists everyone, who contributed to this repo and wanted to show up her - Hugo Salou - Dimitri Belopopsky + Henrik Abel Christensen +- Tarun Vellishetty diff --git a/contents/tree_traversal/code/ruby/tree.rb b/contents/tree_traversal/code/ruby/tree.rb new file mode 100644 index 000000000..b88244ef9 --- /dev/null +++ b/contents/tree_traversal/code/ruby/tree.rb @@ -0,0 +1,65 @@ +def create_tree(rows, children) + return { id: rows, children: [] } if rows.zero? + + { + id: rows, + children: Array.new(children, create_tree(rows - 1, children)) + } +end + +def dfs_preorder(tree) + print "#{tree[:id]} " + tree[:children].each { |child| dfs_preorder(child) } +end + +def dfs_postorder(tree) + tree[:children].each { |child| dfs_postorder(child) } + print "#{tree[:id]} " +end + +def dfs_inorder(tree) + return unless tree + + if tree[:children].count > 2 + raise 'Postorder traversal is only valid for binary trees' + end + + dfs_inorder(tree[:children][0]) + print "#{tree[:id]} " + dfs_inorder(tree[:children][1]) +end + +def dfs_iterative(tree) + stack = [tree] + while stack.count.positive? + current = stack.pop + print "#{current[:id]} " + stack.push(*current[:children]) + end +end + +def bfs(tree) + queue = [tree] + while queue.count.positive? + current = queue.shift + print "#{current[:id]} " + queue.push(*current[:children]) + end +end + +root = create_tree(2, 3) +puts "[#]\nRecursive DFS:" +dfs_preorder(root) +puts "" +puts "[#]\nRecursive Postorder DFS:" +dfs_postorder(root) +puts "" +puts "[#]\nStack-based DFS:" +dfs_iterative(root) +puts "" +puts "[#]\nQueue-based DFS:" +bfs(root) +puts "" +root_binary = create_tree(3, 2); +puts "[#]\nRecursive Inorder DFS for Binary Tree:" +dfs_inorder(root_binary) diff --git a/contents/tree_traversal/tree_traversal.md b/contents/tree_traversal/tree_traversal.md index 5dc6657e0..ceeeb509f 100644 --- a/contents/tree_traversal/tree_traversal.md +++ b/contents/tree_traversal/tree_traversal.md @@ -46,6 +46,8 @@ As a note, a `node` struct is not necessary in javascript, so this is an example [import:6-6, lang:"matlab"](code/matlab/tree.m) {% sample lang="coco" %} [import:3-3, lang:"coconut"](code/coconut/tree_traversal.coco) +{% sample lang="ruby" %} +[import:1-8, lang:"ruby"](code/ruby/tree.rb) {% endmethod %} 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 [import:31-45, lang:"matlab"](code/matlab/tree.m) {% sample lang="coco" %} [import:5-9, lang:"coconut"](code/coconut/tree_traversal.coco) +{% sample lang="ruby" %} +[import:10-13, lang:"ruby"](code/ruby/tree.rb) {% endmethod %} 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 [import:47-62, lang:"matlab"](code/matlab/tree.m) {% sample lang="coco" %} [import:11-15, lang:="coconut"](code/coconut/tree_traversal.coco) +{% sample lang="ruby" %} +[import:15-18, lang:"ruby"](code/ruby/tree.rb) {% endmethod %}

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

@@ -260,6 +268,8 @@ In code, it looks like this: [import:84-106, lang:"matlab"](code/matlab/tree.m) {% sample lang="coco" %} [import:32-39, lang:"coconut"](code/coconut/tree_traversal.coco) +{% sample lang="ruby" %} +[import:32-39, lang:"ruby"](code/ruby/tree.rb) {% endmethod %} 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 [import:108-129, lang:"matlab"](code/matlab/tree.m) {% sample lang="coco" %} [import:41-48, lang:"coconut"](code/coconut/tree_traversal.coco) +{% sample lang="ruby" %} +[import:41-48, lang:"ruby"](code/ruby/tree.rb) {% endmethod %} ## Video Explanation @@ -377,6 +389,8 @@ The code snippets were taken from this [Scratch project](https://scratch.mit.edu [import, lang:"matlab"](code/matlab/tree.m) {% sample lang="coco" %} [import, lang:"coconut"](code/coconut/tree_traversal.coco) +{% sample lang="ruby" %} +[import, lang:"ruby"](code/ruby/tree.rb) {% endmethod %}