Skip to content

Commit cfe29aa

Browse files
Ken Powerkenpower
authored andcommitted
scala implementation of tree traversal
1 parent 442a170 commit cfe29aa

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import scala.collection.mutable._
2+
3+
object TreeTraversal {
4+
5+
class Tree(val rowCount: Int, val childrenCount: Int) {
6+
7+
private case class Node(var id: Int) {
8+
9+
var children = ListBuffer[Node]()
10+
}
11+
12+
private val root: Node = Node(1)
13+
createAllChildren(root, rowCount, childrenCount)
14+
15+
private def createAllChildren(node: Node, rowCount: Int, childrenCount: Int): Unit = {
16+
if (rowCount <= 1) return
17+
18+
0 until childrenCount foreach { i =>
19+
node.children += Node(node.id * 10 + i + 1)
20+
createAllChildren(node.children(i), rowCount - 1, childrenCount)
21+
}
22+
}
23+
24+
private def doSomethingWithNode(node: Node) = Console.println(node.id)
25+
26+
def dfsRecursive(): Unit = {
27+
def dfsRecursive(node: Node): Unit = {
28+
doSomethingWithNode(node)
29+
node.children.foreach(dfsRecursive)
30+
}
31+
32+
dfsRecursive(root)
33+
}
34+
35+
def dfsRecursivePostOrder(): Unit = {
36+
def dfsRecursivePostOrder(node: Node): Unit = {
37+
node.children.foreach(dfsRecursivePostOrder)
38+
doSomethingWithNode(node)
39+
}
40+
41+
dfsRecursivePostOrder(root)
42+
}
43+
44+
def dfsRecursiveInOrderBinary(): Unit = {
45+
def processIfChildExists(children: ListBuffer[Node], index: Int) =
46+
if (children.isDefinedAt(index))
47+
dfsRecursiveInOrderBinary(children(index))
48+
49+
def dfsRecursiveInOrderBinary(node: Node): Unit = {
50+
if (node.children.size > 2)
51+
throw new Exception("Not a binary tree!")
52+
53+
processIfChildExists(node.children, 0)
54+
doSomethingWithNode(node)
55+
processIfChildExists(node.children, 1)
56+
}
57+
58+
dfsRecursiveInOrderBinary(this.root)
59+
}
60+
61+
def dfsStack(): Unit = {
62+
val stack = new ArrayBuffer[Node]()
63+
stack += root
64+
while (stack.nonEmpty) {
65+
doSomethingWithNode(stack(0))
66+
stack ++= stack.remove(0).children
67+
}
68+
}
69+
70+
def bfsQueue(): Unit = {
71+
val queue = new Queue[Node]()
72+
queue.enqueue(root)
73+
while (queue.nonEmpty) {
74+
doSomethingWithNode(queue.head)
75+
queue ++= queue.dequeue.children
76+
}
77+
}
78+
79+
}
80+
81+
def main(args: Array[String]): Unit = {
82+
Console.println("Creating Tree")
83+
var tree = new Tree(3, 3)
84+
85+
Console.println("Using recursive DFS :")
86+
tree.dfsRecursive
87+
88+
Console.println("Using stack-based DFS :")
89+
tree.dfsStack
90+
91+
Console.println("Using queue-based BFS :")
92+
tree.bfsQueue
93+
94+
Console.println("Using post-order recursive DFS :")
95+
tree.dfsRecursivePostOrder
96+
97+
// Uncommenting the following 2 lines will result in an exception thrown because at least one Node of the Tree
98+
// has more than 2 children and therefor a DFSRecursiveInorderBinary doesn't work.
99+
Console.println("Using in-order binary recursive DFS : (fail)")
100+
tree.dfsRecursiveInOrderBinary
101+
102+
tree = new Tree(3, 2)
103+
Console.println("Using in-order binary recursive DFS : (succeed)")
104+
tree.dfsRecursiveInOrderBinary
105+
}
106+
107+
}

contents/tree_traversal/tree_traversal.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ As a note, a `node` struct is not necessary in javascript, so this is an example
4444
[import:6-6, lang:"matlab"](code/matlab/tree.m)
4545
{% sample lang="coco" %}
4646
[import:3-3, lang:"coconut"](code/coconut/tree_traversal.coco)
47+
{% sample lang="scala" %}
48+
[import:7-10, lang:"scala"](code/scala/tree.scala)
4749
{% endmethod %}
4850

4951
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:
@@ -89,6 +91,8 @@ Because of this, the most straightforward way to traverse the tree might be recu
8991
[import:31-45, lang:"matlab"](code/matlab/tree.m)
9092
{% sample lang="coco" %}
9193
[import:5-9, lang:"coconut"](code/coconut/tree_traversal.coco)
94+
{% sample lang="scala" %}
95+
[import:26-33, lang:"scala"](code/scala/tree.scala)
9296
{% endmethod %}
9397

9498
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:
@@ -143,6 +147,8 @@ Now, in this case the first element searched through is still the root of the tr
143147
[import:47-62, lang:"matlab"](code/matlab/tree.m)
144148
{% sample lang="coco" %}
145149
[import:11-15, lang:="coconut"](codo/coconut/tree_traversal.coco)
150+
{% sample lang="scala" %}
151+
[import:35-42, lang:"scala"](code/scala/tree.scala)
146152
{% endmethod %}
147153

148154
<p>
@@ -192,6 +198,8 @@ In this case, the first node visited is at the bottom of the tree and moves up t
192198
[import:64-82, lang:"matlab"](code/matlab/tree.m)
193199
{% sample lang="coco" %}
194200
[import:17-30, lang:"coconut"](code/coconut/tree_traversal.coco)
201+
{% sample lang="scala" %}
202+
[import:44-59, lang:"scala"](code/scala/tree.scala)
195203
{% endmethod %}
196204

197205
<p>
@@ -250,6 +258,8 @@ In code, it looks like this:
250258
[import:84-106, lang:"matlab"](code/matlab/tree.m)
251259
{% sample lang="coco" %}
252260
[import:32-39, lang:"coconut"](code/coconut/tree_traversal.coco)
261+
{% sample lang="scala" %}
262+
[import:62-70, lang:"scala"](code/scala/tree.scala)
253263
{% endmethod %}
254264

255265
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:
@@ -301,6 +311,8 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can
301311
[import:108-129, lang:"matlab"](code/matlab/tree.m)
302312
{% sample lang="coco" %}
303313
[import:41-48, lang:"coconut"](code/coconut/tree_traversal.coco)
314+
{% sample lang="scala" %}
315+
[import:71-78, lang:"scala"](code/scala/tree.scala)
304316
{% endmethod %}
305317

306318
## Video Explanation
@@ -365,9 +377,12 @@ The code snippets were taken from this [Scratch project](https://scratch.mit.edu
365377
[import, lang:"matlab"](code/matlab/tree.m)
366378
{% sample lang="coco" %}
367379
[import, lang:"coconut"](code/coconut/tree_traversal.coco)
380+
{% sample lang="scala" %}
381+
[import, lang:"scala"](code/scala/tree.scala)
368382
{% endmethod %}
369383

370384

385+
371386
<script>
372387
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
373388
</script>

0 commit comments

Comments
 (0)