5: Implement post-order traversal algorithm (#16)

This commit is contained in:
Adrian Kuta 2022-12-16 18:57:34 +01:00 committed by GitHub
parent 8a2710339a
commit b69e838a8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 8 deletions

View File

@ -1,5 +1,9 @@
package com.github.adriankuta package com.github.adriankuta
import com.github.adriankuta.iterators.PostOrderTreeIterator
import com.github.adriankuta.iterators.PreOrderTreeIterator
import com.github.adriankuta.iterators.TreeNodeIterators
import com.github.adriankuta.iterators.TreeNodeIterators.*
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
open class TreeNode<T>(val value: T) : Iterable<TreeNode<T>>, ChildDeclarationInterface<T> { open class TreeNode<T>(val value: T) : Iterable<TreeNode<T>>, ChildDeclarationInterface<T> {
@ -18,6 +22,8 @@ open class TreeNode<T>(val value: T) : Iterable<TreeNode<T>>, ChildDeclarationIn
val children: List<TreeNode<T>> val children: List<TreeNode<T>>
get() = _children get() = _children
var defaultIterator: TreeNodeIterators = PreOrder
/** /**
* Add new child to current node or root. * Add new child to current node or root.
* *
@ -139,5 +145,8 @@ open class TreeNode<T>(val value: T) : Iterable<TreeNode<T>>, ChildDeclarationIn
* Output: 1 2 5 10 6 11 12 13 3 4 7 8 9 * Output: 1 2 5 10 6 11 12 13 3 4 7 8 9
* ``` * ```
*/ */
override fun iterator(): Iterator<TreeNode<T>> = PreOrderTreeIterator(this) override fun iterator(): Iterator<TreeNode<T>> = when (defaultIterator) {
PreOrder -> PreOrderTreeIterator(this)
PostOrder -> PostOrderTreeIterator(this)
}
} }

View File

@ -1,5 +1,6 @@
package com.github.adriankuta package com.github.adriankuta
import com.github.adriankuta.iterators.TreeNodeIterators
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
typealias ChildDeclaration<T> = ChildDeclarationInterface<T>.() -> Unit typealias ChildDeclaration<T> = ChildDeclarationInterface<T>.() -> Unit
@ -13,8 +14,13 @@ typealias ChildDeclaration<T> = ChildDeclarationInterface<T>.() -> Unit
* @see [ChildDeclarationInterface.child] * @see [ChildDeclarationInterface.child]
*/ */
@JvmSynthetic @JvmSynthetic
inline fun<reified T> tree(root: T, childDeclaration: ChildDeclaration<T>): TreeNode<T> { inline fun <reified T> tree(
root: T,
defaultIterator: TreeNodeIterators = TreeNodeIterators.PreOrder,
childDeclaration: ChildDeclaration<T>
): TreeNode<T> {
val treeNode = TreeNode(root) val treeNode = TreeNode(root)
treeNode.defaultIterator = defaultIterator
treeNode.childDeclaration() treeNode.childDeclaration()
return treeNode return treeNode
} }

View File

@ -0,0 +1,50 @@
package com.github.adriankuta.iterators
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.
* ```
* 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
* ```
*/
class PostOrderTreeIterator<T>(root: TreeNode<T>) : Iterator<TreeNode<T>> {
private val stack = ArrayDeque<TreeNode<T>>()
init {
stack.addAll(getChildrenStack(root))
}
override fun hasNext(): Boolean = stack.isNotEmpty()
override fun next(): TreeNode<T> {
return stack.removeFirst()
}
private fun getChildrenStack(node: TreeNode<T>): ArrayDeque<TreeNode<T>> {
val stack = ArrayDeque<TreeNode<T>>()
if(node.children.isEmpty()) {
return ArrayDeque(listOf(node))
}
node.children.forEach {
stack.addAll(getChildrenStack(it))
}
stack.addLast(node)
return stack
}
}

View File

@ -1,4 +1,6 @@
package com.github.adriankuta package com.github.adriankuta.iterators
import com.github.adriankuta.TreeNode
/** /**
* Tree is iterated by using `Pre-order Traversal Algorithm" * Tree is iterated by using `Pre-order Traversal Algorithm"
@ -31,10 +33,12 @@ class PreOrderTreeIterator<T>(root: TreeNode<T>) : Iterator<TreeNode<T>> {
override fun hasNext(): Boolean = stack.isNotEmpty() override fun hasNext(): Boolean = stack.isNotEmpty()
override fun next(): TreeNode<T> { override fun next(): TreeNode<T> {
println(stack)
val node = stack.removeLast() val node = stack.removeLast()
node.children node.children
.asReversed() .asReversed()
.forEach { stack.addLast(it) } .forEach { stack.addLast(it) }
println(stack)
return node return node
} }
} }

View File

@ -0,0 +1,5 @@
package com.github.adriankuta.iterators
enum class TreeNodeIterators {
PreOrder, PostOrder
}

View File

@ -1,6 +1,8 @@
package com.github.adriankuta package com.github.adriankuta
import com.github.adriankuta.iterators.TreeNodeIterators
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNull import kotlin.test.assertNull
@ -46,9 +48,7 @@ class TreeNodeTest {
"Pretty print test" "Pretty print test"
) )
println("Remove: ${curdNode.value}")
root.removeChild(curdNode) root.removeChild(curdNode)
println("Remove: ${gingerTeaNode.value}")
root.removeChild(gingerTeaNode) root.removeChild(gingerTeaNode)
assertEquals( assertEquals(
"Root\n" + "Root\n" +
@ -87,9 +87,6 @@ class TreeNodeTest {
curdNode.addChild(yogurtNode) curdNode.addChild(yogurtNode)
curdNode.addChild(lassiNode) curdNode.addChild(lassiNode)
println(root.toString())
println(curdNode.height())
root.clear() root.clear()
assertEquals(root.children, emptyList()) assertEquals(root.children, emptyList())
assertEquals(beveragesNode.children, emptyList()) assertEquals(beveragesNode.children, emptyList())
@ -141,4 +138,43 @@ class TreeNodeTest {
} }
assertEquals(root.prettyString(), rootExt.prettyString()) assertEquals(root.prettyString(), rootExt.prettyString())
} }
@Test
fun preOrderIteratorTest() {
val tree = tree("F") {
child("B") {
child("A")
child("D") {
child("C")
child("E")
}
}
child("G") {
child("I") {
child("H")
}
}
}
val expectedPreOrder = listOf("F", "B", "A", "D", "C", "E", "G", "I", "H")
assertContentEquals(expectedPreOrder, tree.toList().map { it.toString() })
}
@Test
fun postOrderIteratorTest() {
val tree = tree("A", TreeNodeIterators.PostOrder) {
child("B") {
child("E")
}
child("C")
child("D") {
child("F")
child("G")
child("H")
child("I")
child("J")
}
}
val expectedPreOrder = listOf("E", "B", "C", "F", "G", "H", "I", "J", "D", "A")
assertContentEquals(expectedPreOrder, tree.toList().map { it.toString() })
}
} }