Skip to content

Commit 570df70

Browse files
author
Ken Power
committed
scala implementation of tree traversal
1 parent 3e4c4f7 commit 570df70

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed
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

+15
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ As a note, a `node` struct is not necessary in javascript, so this is an example
3636
[import:5-8, lang:"go"](code/golang/treetraversal.go)
3737
{% sample lang="asm-x64" %}
3838
[import:24-27, lang:"asm-x64"](code/asm-x64/tree_traversal.s)
39+
{% sample lang="scala" %}
40+
[import:7-10, lang:"scala"](code/scala/tree.scala)
3941
{% endmethod %}
4042

4143
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:
@@ -74,6 +76,8 @@ Because of this, the most straightforward way to traverse the tree might be recu
7476
[import:10-15, lang:"go"](code/golang/treetraversal.go)
7577
{% sample lang="asm-x64" %}
7678
[import:290-314, lang:"asm-x64"](code/asm-x64/tree_traversal.s)
79+
{% sample lang="scala" %}
80+
[import:26-33, lang:"scala"](code/scala/tree.scala)
7781
{% endmethod %}
7882

7983
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:
@@ -120,6 +124,8 @@ Now, in this case the first element searched through is still the root of the tr
120124
[import:17-22, lang:"go"](code/golang/treetraversal.go)
121125
{% sample lang="asm-x64" %}
122126
[import:316-344, lang:"asm-x64"](code/asm-x64/tree_traversal.s)
127+
{% sample lang="scala" %}
128+
[import:35-42, lang:"scala"](code/scala/tree.scala)
123129
{% endmethod %}
124130

125131
<p>
@@ -161,6 +167,8 @@ In this case, the first node visited is at the bottom of the tree and moves up t
161167
[import:24-38, lang:"go"](code/golang/treetraversal.go)
162168
{% sample lang="asm-x64" %}
163169
[import:346-396, lang:"asm-x64"](code/asm-x64/tree_traversal.s)
170+
{% sample lang="scala" %}
171+
[import:44-59, lang:"scala"](code/scala/tree.scala)
164172
{% endmethod %}
165173

166174
<p>
@@ -212,6 +220,8 @@ In code, it looks like this:
212220
[import:40-49, lang:"go"](code/golang/treetraversal.go)
213221
{% sample lang="asm-x64" %}
214222
[import:398-445, lang:"asm-x64"](code/asm-x64/tree_traversal.s)
223+
{% sample lang="scala" %}
224+
[import:62-70, lang:"scala"](code/scala/tree.scala)
215225
{% endmethod %}
216226

217227
All this said, there are a few details about DFS that might not be idea, 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:
@@ -255,6 +265,8 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can
255265
[import:51-60, lang:"go"](code/golang/treetraversal.go)
256266
{% sample lang="asm-x64" %}
257267
[import:447-498, lang:"asm-x64"](code/asm-x64/tree_traversal.s)
268+
{% sample lang="scala" %}
269+
[import:71-78, lang:"scala"](code/scala/tree.scala)
258270
{% endmethod %}
259271

260272
## Example Code
@@ -300,9 +312,12 @@ The code snippets were taken from this [Scratch project](https://scratch.mit.edu
300312
[import, lang:"go"](code/golang/treetraversal.go)
301313
{% sample lang="asm-x64" %}
302314
[import, lang:"asm-x64"](code/asm-x64/tree_traversal.s)
315+
{% sample lang="scala" %}
316+
[import, lang:"scala"](code/scala/tree.scala)
303317
{% endmethod %}
304318

305319

320+
306321
<script>
307322
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
308323
</script>

0 commit comments

Comments
 (0)