diff --git a/contents/tree_traversal/code/scala/tree.scala b/contents/tree_traversal/code/scala/tree.scala new file mode 100644 index 000000000..54a97a842 --- /dev/null +++ b/contents/tree_traversal/code/scala/tree.scala @@ -0,0 +1,106 @@ +import scala.collection.mutable._ + +object TreeTraversal { + + class Tree(val rowCount: Int = 0, val childrenCount: Int = 0) { + + private case class Node(var id: String) { + + var children = ListBuffer[Node]() + } + + private val root: Node = Node("root") + + createAllChildren(root, rowCount, childrenCount) + + private def createAllChildren(node: Node, rowCount: Int, childrenCount: Int): Unit = { + if (rowCount <= 1) return + + 0 until childrenCount foreach { i => + node.children += Node(node.id + "-" + i) + createAllChildren(node.children(i), rowCount - 1, childrenCount) + } + } + + private def doSomethingWithNode(node: Node) = Console.println(node.id) + + def dfsRecursive: Unit = { + def dfsRecursive(node: Node): Unit = { + doSomethingWithNode(node) + node.children.foreach(dfsRecursive) + } + + dfsRecursive(root) + } + + def dfsRecursivePostOrder: Unit = { + def dfsRecursivePostOrder(node: Node): Unit = { + node.children.foreach(dfsRecursivePostOrder) + doSomethingWithNode(node) + } + + dfsRecursivePostOrder(root) + } + + def dfsRecursiveInOrderBinary: Unit = { + def processIfChildExists(children: ListBuffer[Node], index: Int) = + if (children.isDefinedAt(index)) + dfsRecursiveInOrderBinary(children(index)) + + def dfsRecursiveInOrderBinary(node: Node): Unit = { + if (node.children.size > 2) + throw new Exception("Not a binary tree!") + + processIfChildExists(node.children, 0) + doSomethingWithNode(node) + processIfChildExists(node.children, 1) + } + + dfsRecursiveInOrderBinary(this.root) + } + + def dfsStack: Unit = { + val stack = new ArrayBuffer[Node]() + stack += root + while (stack.nonEmpty) { + doSomethingWithNode(stack(0)) + val firstNode = stack.remove(0) + stack ++= firstNode.children + } + } + + def bfsQueue: Unit = { + val queue = new Queue[Node]() + queue.enqueue(root) + while (queue.nonEmpty) { + doSomethingWithNode(queue.head) + val firstNode = queue.dequeue() + queue ++= firstNode.children + } + } + + } + + def main(args: Array[String]): Unit = { + Console.println("Creating Tree") + var theTree = new Tree(3, 3) + + Console.println("Using recursive DFS :") + theTree.dfsRecursive + + Console.println("Using stack-based DFS :") + theTree.dfsStack + + Console.println("Using queue-based BFS :") + theTree.bfsQueue + + Console.println("Using post-order recursive DFS :") + theTree.dfsRecursivePostOrder + + //Create a binary tree to test inOrder traversal + theTree = new Tree(3, 2) + Console.println("Using in-order binary recursive DFS :") + theTree.dfsRecursiveInOrderBinary + } + +} \ No newline at end of file diff --git a/contents/tree_traversal/tree_traversal.md b/contents/tree_traversal/tree_traversal.md index 2defe9da8..72484cc9e 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="scala" %} +[import:7-10, lang:"scala"](code/scala/tree.scala) {% 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="scala" %} +[import:26-34, lang:"scala"](code/scala/tree.scala) {% 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: @@ -148,6 +152,9 @@ Now, in this case the first element searched through is still the root of the tr {% sample lang="m" %} [import:47-62, lang:"matlab"](code/matlab/tree.m) {% sample lang="coco" %} +[import:11-15, lang:="coconut"](codo/coconut/tree_traversal.coco) +{% sample lang="scala" %} +[import:36-43, lang:"scala"](code/scala/tree.scala) [import:11-15, lang:="coconut"](code/coconut/tree_traversal.coco) {% endmethod %} @@ -200,6 +207,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="scala" %} +[import:45-60, lang:"scala"](code/scala/tree.scala) {% endmethod %}
@@ -260,6 +269,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="scala" %} +[import:62-70, lang:"scala"](code/scala/tree.scala) {% 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 +324,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="scala" %} +[import:72-80, lang:"scala"](code/scala/tree.scala) {% endmethod %} ## Video Explanation @@ -377,6 +390,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="scala" %} +[import, lang:"scala"](code/scala/tree.scala) {% endmethod %}