From cfe29aabd08d837b42683df8f4f051f4ddf488af Mon Sep 17 00:00:00 2001 From: Ken Power Date: Mon, 22 Oct 2018 13:43:19 +0200 Subject: [PATCH 1/4] scala implementation of tree traversal --- contents/tree_traversal/code/scala/tree.scala | 107 ++++++++++++++++++ contents/tree_traversal/tree_traversal.md | 15 +++ 2 files changed, 122 insertions(+) create mode 100644 contents/tree_traversal/code/scala/tree.scala diff --git a/contents/tree_traversal/code/scala/tree.scala b/contents/tree_traversal/code/scala/tree.scala new file mode 100644 index 000000000..14a2da840 --- /dev/null +++ b/contents/tree_traversal/code/scala/tree.scala @@ -0,0 +1,107 @@ +import scala.collection.mutable._ + +object TreeTraversal { + + class Tree(val rowCount: Int, val childrenCount: Int) { + + private case class Node(var id: Int) { + + var children = ListBuffer[Node]() + } + + private val root: Node = Node(1) + 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 * 10 + i + 1) + 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)) + stack ++= stack.remove(0).children + } + } + + def bfsQueue(): Unit = { + val queue = new Queue[Node]() + queue.enqueue(root) + while (queue.nonEmpty) { + doSomethingWithNode(queue.head) + queue ++= queue.dequeue.children + } + } + + } + + def main(args: Array[String]): Unit = { + Console.println("Creating Tree") + var tree = new Tree(3, 3) + + Console.println("Using recursive DFS :") + tree.dfsRecursive + + Console.println("Using stack-based DFS :") + tree.dfsStack + + Console.println("Using queue-based BFS :") + tree.bfsQueue + + Console.println("Using post-order recursive DFS :") + tree.dfsRecursivePostOrder + + // Uncommenting the following 2 lines will result in an exception thrown because at least one Node of the Tree + // has more than 2 children and therefor a DFSRecursiveInorderBinary doesn't work. + Console.println("Using in-order binary recursive DFS : (fail)") + tree.dfsRecursiveInOrderBinary + + tree = new Tree(3, 2) + Console.println("Using in-order binary recursive DFS : (succeed)") + tree.dfsRecursiveInOrderBinary + } + +} diff --git a/contents/tree_traversal/tree_traversal.md b/contents/tree_traversal/tree_traversal.md index 3cc5a2fbb..9aa179995 100644 --- a/contents/tree_traversal/tree_traversal.md +++ b/contents/tree_traversal/tree_traversal.md @@ -44,6 +44,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: @@ -89,6 +91,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-33, 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: @@ -143,6 +147,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"](codo/coconut/tree_traversal.coco) +{% sample lang="scala" %} +[import:35-42, lang:"scala"](code/scala/tree.scala) {% endmethod %}

@@ -192,6 +198,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:44-59, lang:"scala"](code/scala/tree.scala) {% endmethod %}

@@ -250,6 +258,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: @@ -301,6 +311,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:71-78, lang:"scala"](code/scala/tree.scala) {% endmethod %} ## Video Explanation @@ -365,9 +377,12 @@ 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 %} + From f06d5304e0761bdf742f43b855abebd6c052f28e Mon Sep 17 00:00:00 2001 From: Ken Power Date: Sun, 21 Mar 2021 22:58:33 +0000 Subject: [PATCH 2/4] updated with requested PR changes #531 --- contents/tree_traversal/code/scala/tree.scala | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/contents/tree_traversal/code/scala/tree.scala b/contents/tree_traversal/code/scala/tree.scala index 14a2da840..3260aed22 100644 --- a/contents/tree_traversal/code/scala/tree.scala +++ b/contents/tree_traversal/code/scala/tree.scala @@ -4,26 +4,27 @@ object TreeTraversal { class Tree(val rowCount: Int, val childrenCount: Int) { - private case class Node(var id: Int) { + private case class Node(var id: String) { var children = ListBuffer[Node]() } - private val root: Node = Node(1) + private val root: Node = Node("root") + createAllChildren(root, rowCount, childrenCount) - private def createAllChildren(node: Node, rowCount: Int, childrenCount: Int): Unit = { + private def createAllChildren(node: Node, rowCount: Int = 0, childrenCount: Int = 0): Unit = { if (rowCount <= 1) return 0 until childrenCount foreach { i => - node.children += Node(node.id * 10 + i + 1) + 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: Unit = { def dfsRecursive(node: Node): Unit = { doSomethingWithNode(node) node.children.foreach(dfsRecursive) @@ -32,7 +33,7 @@ object TreeTraversal { dfsRecursive(root) } - def dfsRecursivePostOrder(): Unit = { + def dfsRecursivePostOrder: Unit = { def dfsRecursivePostOrder(node: Node): Unit = { node.children.foreach(dfsRecursivePostOrder) doSomethingWithNode(node) @@ -41,7 +42,7 @@ object TreeTraversal { dfsRecursivePostOrder(root) } - def dfsRecursiveInOrderBinary(): Unit = { + def dfsRecursiveInOrderBinary: Unit = { def processIfChildExists(children: ListBuffer[Node], index: Int) = if (children.isDefinedAt(index)) dfsRecursiveInOrderBinary(children(index)) @@ -58,21 +59,23 @@ object TreeTraversal { dfsRecursiveInOrderBinary(this.root) } - def dfsStack(): Unit = { + def dfsStack: Unit = { val stack = new ArrayBuffer[Node]() stack += root while (stack.nonEmpty) { doSomethingWithNode(stack(0)) - stack ++= stack.remove(0).children + val firstNode = stack.remove(0) + stack ++= firstNode.children } } - def bfsQueue(): Unit = { + def bfsQueue: Unit = { val queue = new Queue[Node]() queue.enqueue(root) while (queue.nonEmpty) { doSomethingWithNode(queue.head) - queue ++= queue.dequeue.children + val firstNode = queue.dequeue() + queue ++= firstNode.children } } @@ -80,28 +83,24 @@ object TreeTraversal { def main(args: Array[String]): Unit = { Console.println("Creating Tree") - var tree = new Tree(3, 3) + var theTree = new Tree(3, 3) Console.println("Using recursive DFS :") - tree.dfsRecursive + theTree.dfsRecursive Console.println("Using stack-based DFS :") - tree.dfsStack + theTree.dfsStack Console.println("Using queue-based BFS :") - tree.bfsQueue + theTree.bfsQueue Console.println("Using post-order recursive DFS :") - tree.dfsRecursivePostOrder - - // Uncommenting the following 2 lines will result in an exception thrown because at least one Node of the Tree - // has more than 2 children and therefor a DFSRecursiveInorderBinary doesn't work. - Console.println("Using in-order binary recursive DFS : (fail)") - tree.dfsRecursiveInOrderBinary - - tree = new Tree(3, 2) - Console.println("Using in-order binary recursive DFS : (succeed)") - tree.dfsRecursiveInOrderBinary + 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 From 336ef50912fb94aed56206d087218c5295ae695e Mon Sep 17 00:00:00 2001 From: Ken Power Date: Sun, 21 Mar 2021 23:32:21 +0000 Subject: [PATCH 3/4] add scala to .md --- contents/tree_traversal/tree_traversal.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/contents/tree_traversal/tree_traversal.md b/contents/tree_traversal/tree_traversal.md index 9aa179995..1dd69ae93 100644 --- a/contents/tree_traversal/tree_traversal.md +++ b/contents/tree_traversal/tree_traversal.md @@ -92,7 +92,7 @@ Because of this, the most straightforward way to traverse the tree might be recu {% sample lang="coco" %} [import:5-9, lang:"coconut"](code/coconut/tree_traversal.coco) {% sample lang="scala" %} -[import:26-33, lang:"scala"](code/scala/tree.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,7 +148,7 @@ Now, in this case the first element searched through is still the root of the tr {% sample lang="coco" %} [import:11-15, lang:="coconut"](codo/coconut/tree_traversal.coco) {% sample lang="scala" %} -[import:35-42, lang:"scala"](code/scala/tree.scala) +[import:36-43, lang:"scala"](code/scala/tree.scala) {% endmethod %}

@@ -199,7 +199,7 @@ In this case, the first node visited is at the bottom of the tree and moves up t {% sample lang="coco" %} [import:17-30, lang:"coconut"](code/coconut/tree_traversal.coco) {% sample lang="scala" %} -[import:44-59, lang:"scala"](code/scala/tree.scala) +[import:45-60, lang:"scala"](code/scala/tree.scala) {% endmethod %}

@@ -312,7 +312,7 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can {% sample lang="coco" %} [import:41-48, lang:"coconut"](code/coconut/tree_traversal.coco) {% sample lang="scala" %} -[import:71-78, lang:"scala"](code/scala/tree.scala) +[import:72-80, lang:"scala"](code/scala/tree.scala) {% endmethod %} ## Video Explanation @@ -382,7 +382,6 @@ The code snippets were taken from this [Scratch project](https://scratch.mit.edu {% endmethod %} - From 2a81c9b906ca5ce92b12982cd55019d4b5e132b2 Mon Sep 17 00:00:00 2001 From: Ken Power Date: Sun, 21 Mar 2021 23:53:50 +0000 Subject: [PATCH 4/4] added default values --- contents/tree_traversal/code/scala/tree.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contents/tree_traversal/code/scala/tree.scala b/contents/tree_traversal/code/scala/tree.scala index 3260aed22..54a97a842 100644 --- a/contents/tree_traversal/code/scala/tree.scala +++ b/contents/tree_traversal/code/scala/tree.scala @@ -2,7 +2,7 @@ import scala.collection.mutable._ object TreeTraversal { - class Tree(val rowCount: Int, val childrenCount: Int) { + class Tree(val rowCount: Int = 0, val childrenCount: Int = 0) { private case class Node(var id: String) { @@ -13,7 +13,7 @@ object TreeTraversal { createAllChildren(root, rowCount, childrenCount) - private def createAllChildren(node: Node, rowCount: Int = 0, childrenCount: Int = 0): Unit = { + private def createAllChildren(node: Node, rowCount: Int, childrenCount: Int): Unit = { if (rowCount <= 1) return 0 until childrenCount foreach { i =>