diff --git a/src/main/kotlin/it/norangeb/algorithms/datastructures/queue/priority/BinaryHeap.kt b/src/main/kotlin/it/norangeb/algorithms/datastructures/queue/priority/BinaryHeap.kt new file mode 100644 index 0000000..eebe71c --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/datastructures/queue/priority/BinaryHeap.kt @@ -0,0 +1,243 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.datastructures.queue.priority + +import arrow.core.None +import arrow.core.Option +import arrow.core.toOption + +class BinaryHeap private constructor( + private var array: Array, + private val shouldExchange: (T, T) -> Boolean +) : PriorityQueue { + private var size: Int = 0 + + override fun insert(elem: T) { + if (isFull()) + resizeArray(array.size * WAY) + + array[++size] = elem + pushUp(array, size) + } + + override fun pop(): Option { + if (size < 1) return None + val result = array[FIRST_ELEMENT] + + exchange(array, FIRST_ELEMENT, size) + array[size--] = null + pullDown(array, FIRST_ELEMENT) + + if (isOneQuarterFull()) + resizeArray(array.size / WAY) + + return result.toOption() + } + + private fun print() { + print("|") + array.forEach { + print(" $it |") + } + println() + } + + override fun peek(): Option = if (size >= 1) + array[1].toOption() + else + None + + override fun isEmpty(): Boolean = size == 0 + + override fun size(): Int = size + + private fun pushUp(array: Array, k: Int) { + when { + k <= 1 -> return + shouldExchange( + array[k / WAY] ?: return, + array[k] ?: return + ) -> { + exchange(array, k / WAY, k) + pushUp(array, k / WAY) + } + else -> return + } + } + + private fun pullDown(array: Array, k: Int) { + var i = k + while (i * WAY <= size) { + val child = i * WAY + + when { + array[child + 1] == null -> exchange(array, i, child) + shouldExchange( + array[child] ?: return, + array[child + 1] ?: return + ) -> exchange(array, i, child + 1) + else -> exchange(array, i, child) + } + + i *= WAY + } + + pushUp(array, i) + } + + private fun exchange(array: Array, i: Int, j: Int) { + array[i] = array[j].also { + array[j] = array[i] + } + } + + private fun isFull(): Boolean = size + 1 == array.size + + private fun isOneQuarterFull(): Boolean { + return size > 1 && + size + 1 == array.size / 4 + } + + private fun resizeArray(capacity: Int) { + array = array.copyOf(capacity) + } + + companion object : COPriorityQueue { + private const val DEFAULT_CAPACITY = 3 + private const val WAY = 2 + private const val FIRST_ELEMENT = 1 + + override fun > createMaxPriorityQueue(): PriorityQueue { + val array: Array = arrayOfNulls>(DEFAULT_CAPACITY) as Array + return BinaryHeap(array) { t1, t2 -> + t1 < t2 + } + } + + override fun > createMinPriorityQueue(): PriorityQueue { + val array: Array = arrayOfNulls>(DEFAULT_CAPACITY) as Array + return BinaryHeap(array) { t1, t2 -> + t1 > t2 + } + } + + override fun createMaxPriorityQueue(compare: (T, T) -> Int): PriorityQueue { + val array: Array = arrayOfNulls(DEFAULT_CAPACITY) as Array + return BinaryHeap(array) { t1, t2 -> + compare(t1, t2) < 0 + } + } + + override fun createMinPriorityQueue(compare: (T, T) -> Int): PriorityQueue { + val array: Array = arrayOfNulls(DEFAULT_CAPACITY) as Array + return BinaryHeap(array) { t1, t2 -> + compare(t1, t2) > 0 + } + } + + override fun > createMaxPriorityQueue(compareBy: (T) -> C): PriorityQueue { + val array: Array = arrayOfNulls(DEFAULT_CAPACITY) as Array + return BinaryHeap(array) { t1, t2 -> + compareBy(t1) < compareBy(t2) + } + } + + override fun > createMinPriorityQueue(compareBy: (T) -> C): PriorityQueue { + val array: Array = arrayOfNulls(DEFAULT_CAPACITY) as Array + return BinaryHeap(array) { t1, t2 -> + compareBy(t1) > compareBy(t2) + } + } + + override fun > createMaxPriorityQueueFromArray(array: Array): PriorityQueue { + val heap = BinaryHeap.createMaxPriorityQueue() + array.forEach { heap.insert(it) } + return heap + } + + override fun > createMinPriorityQueueFromArray(array: Array): PriorityQueue { + val heap = BinaryHeap.createMinPriorityQueue() + array.forEach { heap.insert(it) } + return heap + } + + override fun createMaxPriorityQueueFromArray( + array: Array, + compare: (T, T) -> Int + ): PriorityQueue { + val initArray: Array = arrayOfNulls(DEFAULT_CAPACITY) as Array + val heap = BinaryHeap(initArray) { t1, t2 -> + compare(t1, t2) < 0 + } + + array.forEach { heap.insert(it) } + + return heap + } + + override fun createMinPriorityQueueFromArray( + array: Array, + compare: (T, T) -> Int + ): PriorityQueue { + val initArray: Array = arrayOfNulls(DEFAULT_CAPACITY) as Array + val heap = BinaryHeap(initArray) { t1, t2 -> + compare(t1, t2) > 0 + } + + array.forEach { heap.insert(it) } + + return heap + } + + override fun > createMaxPriorityQueueFromArray( + array: Array, + compareBy: (T) -> C + ): PriorityQueue { + val initArray: Array = arrayOfNulls(DEFAULT_CAPACITY) as Array + val heap = BinaryHeap(initArray) { t1, t2 -> + compareBy(t1) < compareBy(t2) + } + + array.forEach { heap.insert(it) } + + return heap + } + + override fun > createMinPriorityQueueFromArray( + array: Array, + compareBy: (T) -> C + ): PriorityQueue { + val initArray: Array = arrayOfNulls(DEFAULT_CAPACITY) as Array + val heap = BinaryHeap(initArray) { t1, t2 -> + compareBy(t1) > compareBy(t2) + } + + array.forEach { heap.insert(it) } + + return heap + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/datastructures/queue/priority/PriorityQueue.kt b/src/main/kotlin/it/norangeb/algorithms/datastructures/queue/priority/PriorityQueue.kt new file mode 100644 index 0000000..ad1ff64 --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/datastructures/queue/priority/PriorityQueue.kt @@ -0,0 +1,78 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.datastructures.queue.priority + +import arrow.core.Option + +interface PriorityQueue { + fun insert(elem: T) + fun pop(): Option + fun peek(): Option + fun isEmpty(): Boolean + fun size(): Int +} + +interface COPriorityQueue { + fun > createMaxPriorityQueue(): PriorityQueue + fun > createMinPriorityQueue(): PriorityQueue + fun createMaxPriorityQueue(compare: (T, T) -> Int): PriorityQueue + fun createMinPriorityQueue(compare: (T, T) -> Int): PriorityQueue + fun > createMaxPriorityQueue( + compareBy: (T) -> C + ): PriorityQueue + + fun > createMinPriorityQueue( + compareBy: (T) -> C + ): PriorityQueue + + fun > createMaxPriorityQueueFromArray( + array: Array + ): PriorityQueue + + fun > createMinPriorityQueueFromArray( + array: Array + ): PriorityQueue + + fun createMaxPriorityQueueFromArray( + array: Array, + compare: (T, T) -> Int + ): PriorityQueue + + fun createMinPriorityQueueFromArray( + array: Array, + compare: (T, T) -> Int + ): PriorityQueue + + fun > createMaxPriorityQueueFromArray( + array: Array, + compareBy: (T) -> C + ): PriorityQueue + + fun > createMinPriorityQueueFromArray( + array: Array, + compareBy: (T) -> C + ): PriorityQueue +} \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/datastructures/queue/priority/BinaryHeapTest.kt b/src/test/kotlin/it/norangeb/algorithms/datastructures/queue/priority/BinaryHeapTest.kt new file mode 100644 index 0000000..84cafb6 --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/datastructures/queue/priority/BinaryHeapTest.kt @@ -0,0 +1,362 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.datastructures.queue.priority + +import arrow.core.None +import org.amshove.kluent.`should be equal to` +import org.amshove.kluent.`should equal` +import org.junit.jupiter.api.Test +import kotlin.reflect.full.declaredMemberFunctions +import kotlin.reflect.jvm.isAccessible + +class BinaryHeapTest { + + @Test + fun testExchange() { + val exchange = BinaryHeap::class.declaredMemberFunctions + .find { it.name == "exchange" } + .also { it?.isAccessible = true } + + val binaryHeap = BinaryHeap.createMaxPriorityQueue { _, _ -> -1 } + val array = arrayOf(1, 2) + + exchange?.call(binaryHeap, array, 0, 1) + + array `should equal` arrayOf(2, 1) + } + + @Test + fun testPushUp() { + val pushUp = BinaryHeap::class.declaredMemberFunctions + .find { it.name == "pushUp" } + .also { it?.isAccessible = true } + + val binaryHeap = BinaryHeap.createMaxPriorityQueue { t1, t2 -> + t1.compareTo(t2) + } + val array = arrayOf(Int.MAX_VALUE, 23, 5, 12, 3, 7) + + pushUp?.call(binaryHeap, array, 5) + + array `should equal` arrayOf(Int.MAX_VALUE, 23, 7, 12, 3, 5) + } + + @Test + fun testComparableMax() { + val heap = BinaryHeap.createMaxPriorityQueue() + heap.insert(25) + heap.insert(5) + heap.insert(15) + heap.insert(12) + + heap.pop().map { it `should be equal to` 25 } + heap.pop().map { it `should be equal to` 15 } + heap.size() `should be equal to` 2 + + heap.insert(1) + heap.insert(13) + + heap.peek().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 12 } + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 1 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testComparableMin() { + val heap = BinaryHeap.createMinPriorityQueue() + heap.insert(25) + heap.insert(5) + heap.insert(12) + + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 12 } + + heap.insert(15) + heap.insert(1) + heap.insert(13) + + heap.peek().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 15 } + heap.pop().map { it `should be equal to` 25 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testCompareMax() { + val heap = BinaryHeap.createMaxPriorityQueue { t1, t2 -> t1.compareTo(t2) } + heap.insert(15) + heap.insert(12) + heap.insert(5) + heap.insert(25) + + heap.pop().map { it `should be equal to` 25 } + heap.pop().map { it `should be equal to` 15 } + + heap.insert(13) + heap.insert(1) + + heap.peek().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 12 } + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 1 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testCompareMin() { + val heap = BinaryHeap.createMinPriorityQueue { t1, t2 -> t1.compareTo(t2) } + heap.insert(25) + heap.insert(5) + heap.insert(12) + heap.insert(15) + + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 12 } + + heap.insert(1) + heap.insert(13) + + heap.peek().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 15 } + heap.pop().map { it `should be equal to` 25 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testCompareByMax() { + val heap = BinaryHeap.createMaxPriorityQueue { it } + heap.insert(15) + heap.insert(25) + heap.insert(12) + + heap.pop().map { it `should be equal to` 25 } + heap.pop().map { it `should be equal to` 15 } + + heap.insert(1) + heap.insert(5) + heap.insert(13) + + heap.peek().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 12 } + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 1 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testCompareByMin() { + val heap = BinaryHeap.createMinPriorityQueue { it } + heap.insert(12) + heap.insert(25) + heap.insert(5) + + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 12 } + + heap.insert(13) + heap.insert(15) + heap.insert(1) + + heap.peek().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 15 } + heap.pop().map { it `should be equal to` 25 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testComparableArrayMax() { + val array = arrayOf(5, 12) + val heap = BinaryHeap.createMaxPriorityQueueFromArray(array) + + heap.insert(25) + heap.insert(15) + + heap.pop().map { it `should be equal to` 25 } + heap.pop().map { it `should be equal to` 15 } + + heap.insert(1) + heap.insert(13) + + heap.peek().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 12 } + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 1 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testComparableArrayMin() { + val array = arrayOf(12, 25) + val heap = BinaryHeap.createMinPriorityQueueFromArray(array) + + heap.insert(5) + + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 12 } + + heap.insert(15) + heap.insert(1) + heap.insert(13) + + heap.peek().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 15 } + heap.pop().map { it `should be equal to` 25 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testCompareMaxArray() { + val array = arrayOf(15, 5) + val heap = BinaryHeap.createMaxPriorityQueueFromArray(array) { t1, t2 -> + t1.compareTo(t2) + } + + heap.insert(12) + heap.insert(25) + + heap.pop().map { it `should be equal to` 25 } + heap.pop().map { it `should be equal to` 15 } + + heap.insert(13) + heap.insert(1) + + heap.peek().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 12 } + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 1 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testCompareMinArray() { + val array = arrayOf(15, 12) + val heap = BinaryHeap.createMinPriorityQueueFromArray(array) { t1, t2 -> + t1.compareTo(t2) + } + + heap.insert(25) + heap.insert(5) + + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 12 } + + heap.insert(1) + heap.insert(13) + + heap.peek().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 15 } + heap.pop().map { it `should be equal to` 25 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testCompareByMaxArray() { + val array = arrayOf(12) + val heap = BinaryHeap.createMaxPriorityQueueFromArray(array) { it } + + heap.insert(15) + heap.insert(25) + + heap.pop().map { it `should be equal to` 25 } + heap.pop().map { it `should be equal to` 15 } + + heap.insert(1) + heap.insert(5) + heap.insert(13) + + heap.peek().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 12 } + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 1 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testCompareByMinArray() { + val array = arrayOf(5, 12) + val heap = BinaryHeap.createMinPriorityQueueFromArray(array) { it } + + heap.insert(25) + + heap.pop().map { it `should be equal to` 5 } + heap.pop().map { it `should be equal to` 12 } + + heap.insert(13) + heap.insert(15) + heap.insert(1) + + heap.peek().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 1 } + heap.pop().map { it `should be equal to` 13 } + heap.pop().map { it `should be equal to` 15 } + heap.pop().map { it `should be equal to` 25 } + + (heap.pop() is None) `should be equal to` true + } + + @Test + fun testResize() { + val heap = BinaryHeap.createMaxPriorityQueue() + + (0 until 1000).forEach { heap.insert(it) } + repeat(1010) { heap.pop() } + heap.size() `should be equal to` 0 + heap.insert(10) + heap.insert(11) + + heap.pop().map { it `should be equal to` 11 } + heap.pop().map { it `should be equal to` 10 } + } +} \ No newline at end of file