diff --git a/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/Dictionaries.kt b/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/Dictionaries.kt index 3a3674a..b7f86a9 100644 --- a/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/Dictionaries.kt +++ b/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/Dictionaries.kt @@ -42,6 +42,7 @@ interface OrderedDictionary, V> : Dictionary { fun floor(key: K): Option fun ceiling(key: K): Option fun select(pos: Int): Option + fun rank(key: K): Int fun preOrder(transform: (K) -> R) fun inOrder(transform: (K) -> R) fun postOrder(transform: (K) -> R) diff --git a/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/ImmutableBST.kt b/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/ImmutableBST.kt index 269e696..7abbb70 100644 --- a/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/ImmutableBST.kt +++ b/src/main/kotlin/it/norangeb/algorithms/datastructures/dictionary/ImmutableBST.kt @@ -25,10 +25,7 @@ package it.norangeb.algorithms.datastructures.dictionary -import arrow.core.None -import arrow.core.Option -import arrow.core.Some -import arrow.core.toOption +import arrow.core.* class ImmutableBST, V> : OrderedDictionary { private var root: Option> = None @@ -141,12 +138,59 @@ class ImmutableBST, V> : OrderedDictionary { } } - override fun floor(key: K): Option { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun rank(key: K): Int = rank(root, key) + + private fun rank(node: Option>, key: K): Int = node.map { + when { + it.key == key -> it.child + key < it.key -> rank(it.left, key) + else -> size(it.left) + 1 + rank(it.right, key) + } + }.getOrElse { 0 } + + override fun floor(key: K): Option = floor(root, key).map { it.key } + + private fun floor(node: Option>, key: K): Option> { + return node.flatMap { + when { + it.key == key -> node + key < it.key -> floor(it.left, key) + else -> floorRightIfPossible(it, key) + } + } } - override fun ceiling(key: K): Option { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + private fun floorRightIfPossible( + node: Node, + key: K + ): Option> { + val possibleFlor = floor(node.right, key) + return if (possibleFlor is Some) + possibleFlor + else + node.toOption() + } + + override fun ceiling(key: K): Option = ceiling(root, key).map { it.key } + + private fun ceiling(node: Option>, key: K): Option> { + return node.flatMap { + when { + it.key == key -> node + key > it.key -> ceiling(it.right, key) + else -> ceilingLeftIfpossible(it, key) + } + } + } + + private fun ceilingLeftIfpossible( + node: Node, + key: K + ): Option> { + val possibleCeiling = ceiling(node.left, key) + return if (possibleCeiling is Some) + possibleCeiling + else node.toOption() } override fun inOrder(transform: (K) -> R) = inOrder(root, transform) diff --git a/src/test/kotlin/it/norangeb/algorithms/datastructures/dictionary/ImmutableBSTTest.kt b/src/test/kotlin/it/norangeb/algorithms/datastructures/dictionary/ImmutableBSTTest.kt index 1634d3f..b999f12 100644 --- a/src/test/kotlin/it/norangeb/algorithms/datastructures/dictionary/ImmutableBSTTest.kt +++ b/src/test/kotlin/it/norangeb/algorithms/datastructures/dictionary/ImmutableBSTTest.kt @@ -29,7 +29,6 @@ import arrow.core.None import arrow.core.Some import org.amshove.kluent.`should be equal to` import org.amshove.kluent.`should be` -import org.amshove.kluent.should import org.amshove.kluent.shouldEqual import org.junit.jupiter.api.Test @@ -39,11 +38,12 @@ class ImmutableBSTTest { @Test fun test() { val orderedMap: OrderedDictionary = ImmutableBST() + orderedMap.isEmpty() `should be` true orderedMap.size() `should be equal to` 0 orderedMap.contains("UNO") `should be` false - orderedMap.max() `should be` None - orderedMap.min() `should be` None + orderedMap.max() shouldEqual None + orderedMap.min() shouldEqual None orderedMap["QUATTRO"] = 4 orderedMap["UNO"] = 0 @@ -54,13 +54,13 @@ class ImmutableBSTTest { orderedMap["UNO"] = 1 orderedMap.size() `should be equal to` 3 - orderedMap["UNO"] should { this == Some(1) } + orderedMap["UNO"] shouldEqual Some(1) orderedMap["DUE"] = 2 - orderedMap["DUE"] should { this == Some(2) } - orderedMap.max() should { this == Some("UNO") } - orderedMap.min() should { this == Some("DUE") } + orderedMap["DUE"] shouldEqual Some(2) + orderedMap.max() shouldEqual Some("UNO") + orderedMap.min() shouldEqual Some("DUE") } @Test @@ -88,13 +88,13 @@ class ImmutableBSTTest { orderedMap.delete(1) orderedMap.size() `should be equal to` 8 - orderedMap.min() should { this == Some(2) } + orderedMap.min() shouldEqual Some(2) //delete node with only right child orderedMap.delete(2) orderedMap.size() `should be equal to` 7 - orderedMap.min() should { this == Some(3) } + orderedMap.min() shouldEqual Some(3) //delete node with two child orderedMap.delete(10) @@ -106,23 +106,27 @@ class ImmutableBSTTest { orderedMap.delete(11) orderedMap.size() `should be equal to` 4 - orderedMap.max() should { this == Some(8) } + orderedMap.max() shouldEqual Some(8) } @Test - fun testOrderedOperation() { + fun testRanAndSelect() { val orderedMap: OrderedDictionary = ImmutableBST() - orderedMap.select(0) `should be` None + orderedMap.select(0) shouldEqual None + orderedMap.rank(0) `should be equal to` 0 orderedMap[9] = 9 orderedMap[4] = 4 orderedMap[14] = 14 - orderedMap.select(0) should { this == Some(4) } - orderedMap.select(1) should { this == Some(9) } - orderedMap.select(2) should { this == Some(14) } - orderedMap.select(3) `should be` None + orderedMap.rank(5) `should be equal to` 1 + orderedMap.rank(20) `should be equal to` 3 + orderedMap.rank(3) `should be equal to` 0 + orderedMap.select(0) shouldEqual Some(4) + orderedMap.select(1) shouldEqual Some(9) + orderedMap.select(2) shouldEqual Some(14) + orderedMap.select(3) shouldEqual None orderedMap[13] = 13 orderedMap[15] = 15 @@ -145,36 +149,63 @@ class ImmutableBSTTest { orderedMap[7] = 7 orderedMap[8] = 8 - orderedMap.select(-1) `should be` None - orderedMap.select(0) should { this == Some(0) } - orderedMap.select(1) should { this == Some(1) } - orderedMap.select(5) should { this == Some(5) } - orderedMap.select(8) should { this == Some(8) } - orderedMap.select(9) should { this == Some(9) } - orderedMap.select(10) should { this == Some(10) } - orderedMap.select(14) should { this == Some(14) } - orderedMap.select(17) should { this == Some(17) } - orderedMap.select(18) should { this == Some(18) } - orderedMap.select(19) `should be` None - - println(orderedMap.select(19)) + orderedMap.select(-1) shouldEqual None + orderedMap.select(0) shouldEqual Some(0) + orderedMap.select(1) shouldEqual Some(1) + orderedMap.select(5) shouldEqual Some(5) + orderedMap.select(8) shouldEqual Some(8) + orderedMap.select(9) shouldEqual Some(9) + orderedMap.select(10) shouldEqual Some(10) + orderedMap.select(14) shouldEqual Some(14) + orderedMap.select(17) shouldEqual Some(17) + orderedMap.select(18) shouldEqual Some(18) + orderedMap.select(19) shouldEqual None val orderList = ArrayList() orderedMap.inOrder { orderList.add(it) } - orderList shouldEqual listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 17, 18) + orderList shouldEqual listOf( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18 + ) val postOrderList = ArrayList() orderedMap.postOrder { postOrderList.add(it) } - postOrderList shouldEqual listOf(0, 1, 2, 3, 8, 7, 6, 5, 4, 10, 11, 12, 13, 18, 17, 16, 15, - 14, 9) + postOrderList shouldEqual listOf( + 0, 1, 2, 3, 8, 7, 6, 5, 4, 10, 11, 12, + 13, 18, 17, 16, 15, 14, 9 + ) val preOrderList = ArrayList() orderedMap.preOrder { preOrderList.add(it) } - preOrderList shouldEqual listOf(9, 4, 3, 2, 1, 0, 5, 6, 7, 8, 14, 13, 12, 11, 10, 15, 16, - 17, 18) + preOrderList shouldEqual listOf( + 9, 4, 3, 2, 1, 0, 5, 6, 7, 8, 14, 13, + 12, 11, 10, 15, 16, 17, 18 + ) + } + + @Test + fun testFloorAndCeiling() { + val orderedMap: OrderedDictionary = ImmutableBST() + + orderedMap.floor('Z') shouldEqual None + orderedMap.ceiling('Z') shouldEqual None + + orderedMap['S'] = true + orderedMap['X'] = true + orderedMap['E'] = true + orderedMap['A'] = true + orderedMap['C'] = true + orderedMap['R'] = true + orderedMap['H'] = true + orderedMap['M'] = true + + orderedMap.floor('G') shouldEqual Some('E') + orderedMap.floor('D') shouldEqual Some('C') + orderedMap.floor('R') shouldEqual Some('R') + orderedMap.ceiling('Q') shouldEqual Some('R') + orderedMap.ceiling('A') shouldEqual Some('A') } } \ No newline at end of file