From 5dd586f9af1b36e033a6f61b65d5a1296806310a Mon Sep 17 00:00:00 2001 From: Adrian Kuta Date: Fri, 16 Dec 2022 19:00:25 +0100 Subject: [PATCH] 4: Implement Level-order traversal algorithm (#17) --- .../kotlin/com.github.adriankuta/TreeNode.kt | 24 ++----- .../iterators/LevelOrderTreeIterator.kt | 39 +++++++++++ .../iterators/PostOrderTreeIterator.kt | 8 +-- .../iterators/PreOrderTreeIterator.kt | 8 +-- .../iterators/TreeNodeIterators.kt | 65 ++++++++++++++++++- .../com.github.adriankuta/TreeNodeTest.kt | 48 ++++++++++++++ 6 files changed, 162 insertions(+), 30 deletions(-) create mode 100644 src/commonMain/kotlin/com.github.adriankuta/iterators/LevelOrderTreeIterator.kt diff --git a/src/commonMain/kotlin/com.github.adriankuta/TreeNode.kt b/src/commonMain/kotlin/com.github.adriankuta/TreeNode.kt index 1b38d20..b8f17d3 100644 --- a/src/commonMain/kotlin/com.github.adriankuta/TreeNode.kt +++ b/src/commonMain/kotlin/com.github.adriankuta/TreeNode.kt @@ -1,5 +1,6 @@ package com.github.adriankuta +import com.github.adriankuta.iterators.LevelOrderTreeIterator import com.github.adriankuta.iterators.PostOrderTreeIterator import com.github.adriankuta.iterators.PreOrderTreeIterator import com.github.adriankuta.iterators.TreeNodeIterators @@ -22,6 +23,9 @@ open class TreeNode(val value: T) : Iterable>, ChildDeclarationIn val children: List> get() = _children + /** + * Choose one of available iterators from [TreeNodeIterators] + */ var defaultIterator: TreeNodeIterators = PreOrder /** @@ -126,27 +130,11 @@ open class TreeNode(val value: T) : Iterable>, ChildDeclarationIn } /** - * Tree is iterated by using `Pre-order Traversal Algorithm" - * 1. Check if the current node is empty or null. - * 2. Display the data part of the root (or current node). - * 3. Traverse the left subtree by recursively calling the pre-order function. - * 4. Traverse the right subtree by recursively calling the pre-order function. - * ``` - * E.g. - * 1 - * / | \ - * / | \ - * 2 3 4 - * / \ / | \ - * 5 6 7 8 9 - * / / | \ - * 10 11 12 13 - * - * Output: 1 2 5 10 6 11 12 13 3 4 7 8 9 - * ``` + * You can change default iterator by changing [defaultIterator] property. */ override fun iterator(): Iterator> = when (defaultIterator) { PreOrder -> PreOrderTreeIterator(this) PostOrder -> PostOrderTreeIterator(this) + LevelOrder -> LevelOrderTreeIterator(this) } } \ No newline at end of file diff --git a/src/commonMain/kotlin/com.github.adriankuta/iterators/LevelOrderTreeIterator.kt b/src/commonMain/kotlin/com.github.adriankuta/iterators/LevelOrderTreeIterator.kt new file mode 100644 index 0000000..5fcf954 --- /dev/null +++ b/src/commonMain/kotlin/com.github.adriankuta/iterators/LevelOrderTreeIterator.kt @@ -0,0 +1,39 @@ +package com.github.adriankuta.iterators + +import com.github.adriankuta.TreeNode + +/** + * Tree is iterated by using `Level-order Traversal Algorithm" + * In level-order traversal we iterating nodes level by level, + * starting from root, and going deeper and deeper in tree. + * ``` + * E.g. + * 1 + * / | \ + * / | \ + * 2 3 4 + * / \ / | \ + * 5 6 7 8 9 + * / / | \ + * 10 11 12 13 + * + * Output: 1 2 3 4 5 6 7 8 9 10 11 12 13 + * ``` + */ +class LevelOrderTreeIterator(root: TreeNode) : Iterator> { + + private val stack = ArrayDeque>() + + init { + stack.addLast(root) + } + + override fun hasNext(): Boolean = stack.isNotEmpty() + + override fun next(): TreeNode { + val node = stack.removeFirst() + node.children + .forEach { stack.addLast(it) } + return node + } +} \ No newline at end of file diff --git a/src/commonMain/kotlin/com.github.adriankuta/iterators/PostOrderTreeIterator.kt b/src/commonMain/kotlin/com.github.adriankuta/iterators/PostOrderTreeIterator.kt index c735e1f..6e54db1 100644 --- a/src/commonMain/kotlin/com.github.adriankuta/iterators/PostOrderTreeIterator.kt +++ b/src/commonMain/kotlin/com.github.adriankuta/iterators/PostOrderTreeIterator.kt @@ -4,10 +4,8 @@ import com.github.adriankuta.TreeNode /** * Tree is iterated by using `Post-order Traversal Algorithm" - * 1. Check if the current node is empty or null. - * 2. Display the data part of the root (or current node). - * 3. Traverse the left subtree by recursively calling the pre-order function. - * 4. Traverse the right subtree by recursively calling the pre-order function. + * In post-order traversal, we starting from most left child. + * First visit all children of parent, then parent. * ``` * E.g. * 1 @@ -19,7 +17,7 @@ import com.github.adriankuta.TreeNode * / / | \ * 10 11 12 13 * - * Output: 1 2 5 10 6 11 12 13 3 4 7 8 9 + * Output: 10 5 11 12 13 6 2 3 7 8 9 4 1 * ``` */ class PostOrderTreeIterator(root: TreeNode) : Iterator> { diff --git a/src/commonMain/kotlin/com.github.adriankuta/iterators/PreOrderTreeIterator.kt b/src/commonMain/kotlin/com.github.adriankuta/iterators/PreOrderTreeIterator.kt index 5e03c22..17e7091 100644 --- a/src/commonMain/kotlin/com.github.adriankuta/iterators/PreOrderTreeIterator.kt +++ b/src/commonMain/kotlin/com.github.adriankuta/iterators/PreOrderTreeIterator.kt @@ -4,10 +4,8 @@ import com.github.adriankuta.TreeNode /** * Tree is iterated by using `Pre-order Traversal Algorithm" - * 1. Check if the current node is empty or null. - * 2. Display the data part of the root (or current node). - * 3. Traverse the left subtree by recursively calling the pre-order function. - * 4. Traverse the right subtree by recursively calling the pre-order function. + * The pre-order traversal is a topologically sorted one, + * because a parent node is processed before any of its child nodes is done. * ``` * E.g. * 1 @@ -33,12 +31,10 @@ class PreOrderTreeIterator(root: TreeNode) : Iterator> { override fun hasNext(): Boolean = stack.isNotEmpty() override fun next(): TreeNode { - println(stack) val node = stack.removeLast() node.children .asReversed() .forEach { stack.addLast(it) } - println(stack) return node } } \ No newline at end of file diff --git a/src/commonMain/kotlin/com.github.adriankuta/iterators/TreeNodeIterators.kt b/src/commonMain/kotlin/com.github.adriankuta/iterators/TreeNodeIterators.kt index 7451c4c..5e3d71f 100644 --- a/src/commonMain/kotlin/com.github.adriankuta/iterators/TreeNodeIterators.kt +++ b/src/commonMain/kotlin/com.github.adriankuta/iterators/TreeNodeIterators.kt @@ -1,5 +1,68 @@ package com.github.adriankuta.iterators +/** + * @see PreOrder + * @see PostOrder + * @see LevelOrder + */ enum class TreeNodeIterators { - PreOrder, PostOrder + /** + * Tree is iterated by using `Pre-order Traversal Algorithm" + * The pre-order traversal is a topologically sorted one, + * because a parent node is processed before any of its child nodes is done. + * ``` + * E.g. + * 1 + * / | \ + * / | \ + * 2 3 4 + * / \ / | \ + * 5 6 7 8 9 + * / / | \ + * 10 11 12 13 + * + * Output: 1 2 5 10 6 11 12 13 3 4 7 8 9 + * ``` + */ + PreOrder, + + /** + * Tree is iterated by using `Post-order Traversal Algorithm" + * In post-order traversal, we starting from most left child. + * First visit all children of parent, then parent. + * ``` + * E.g. + * 1 + * / | \ + * / | \ + * 2 3 4 + * / \ / | \ + * 5 6 7 8 9 + * / / | \ + * 10 11 12 13 + * + * Output: 10 5 11 12 13 6 2 3 7 8 9 4 1 + * ``` + */ + PostOrder, + + /** + * Tree is iterated by using `Level-order Traversal Algorithm" + * In level-order traversal we iterating nodes level by level, + * starting from root, and going deeper and deeper in tree. + * ``` + * E.g. + * 1 + * / | \ + * / | \ + * 2 3 4 + * / \ / | \ + * 5 6 7 8 9 + * / / | \ + * 10 11 12 13 + * + * Output: 1 2 3 4 5 6 7 8 9 10 11 12 13 + * ``` + */ + LevelOrder } \ No newline at end of file diff --git a/src/commonTest/kotlin/com.github.adriankuta/TreeNodeTest.kt b/src/commonTest/kotlin/com.github.adriankuta/TreeNodeTest.kt index 33c24d6..a30fcd6 100644 --- a/src/commonTest/kotlin/com.github.adriankuta/TreeNodeTest.kt +++ b/src/commonTest/kotlin/com.github.adriankuta/TreeNodeTest.kt @@ -177,4 +177,52 @@ class TreeNodeTest { val expectedPreOrder = listOf("E", "B", "C", "F", "G", "H", "I", "J", "D", "A") assertContentEquals(expectedPreOrder, tree.toList().map { it.toString() }) } + + @Test + fun secondPostOrderIteratorTest() { + val tree = tree(1, TreeNodeIterators.PostOrder) { + child(2) { + child(5) { + child(10) + } + child(6) { + child(11) + child(12) + child(13) + } + } + child(3) + child(4) { + child(7) + child(8) + child(9) + } + } + val expectedOrder = listOf(10, 5, 11, 12, 13, 6, 2, 3, 7, 8, 9, 4, 1) + assertContentEquals(expectedOrder, tree.toList().map { it.value }) + } + + @Test + fun levelOrderIteratorTest() { + val tree = tree(1, TreeNodeIterators.LevelOrder) { + child(2) { + child(5) { + child(10) + } + child(6) { + child(11) + child(12) + child(13) + } + } + child(3) + child(4) { + child(7) + child(8) + child(9) + } + } + val expectedOrder = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) + assertContentEquals(expectedOrder, tree.toList().map { it.value }) + } } \ No newline at end of file