All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.collections.binaryHeap.kt Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2021 Dr. David H. Akehurst (http://dr.david.h.akehurst.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.akehurst.language.agl.collections

fun , V> binaryHeapMin(): BinaryHeap = binaryHeap { parent, child ->
    when {
        parent < child -> 1
        parent > child -> -1
        else -> 0
    }
}

fun , V> binaryHeapMax(): BinaryHeap = binaryHeap { parent, child ->
    when {
        parent > child -> 1
        parent < child -> -1
        else -> 0
    }
}

/**
 * comparator: parent,child -> when {
 *   1 -> move parent up
 *  -1 -> move child up
 *   0 -> do nothing
 * }
 */
fun  binaryHeap(comparator: Comparator): BinaryHeap = BinaryHeapComparable(comparator)

infix fun  K.to(that: V): BinaryHeap.Entry = BinaryHeapComparable.Entry(this, that)

interface BinaryHeap : Iterable {

    interface Entry {
        val key: K
        val value: V
    }

    val size: Int

    /**
     * the root of the tree - the (or one of the) element(s) with the minimum key
     * null if the BinaryHeap is empty
     */
    val peekRoot: V?

    val entries: List>

    /**
     * insert(key,value)
     */
    operator fun set(key: K, value: V)

    /**
     * peek(key)
     * order is not predictable, but faster to return a list than a set
     */
    operator fun get(key: K): List

    fun isEmpty(): Boolean
    fun isNotEmpty(): Boolean
    fun insert(key: K, value: V)
    fun peekOneOf(key: K): V?
    fun peekAll(key: K): List
    fun extractRoot(): V?
    fun extractRootAndThenInsert(key: K, value: V): V?
    fun insertAndThenExtractRoot(key: K, value: V): V
    fun remove(key: K): V?

    fun clear()
}

class BinaryHeapComparable(
    val comparator: Comparator //(parent: K, child: K) -> Boolean
) : BinaryHeap {

    class Entry(override val key: K, override val value: V) : BinaryHeap.Entry {
        override fun hashCode(): Int = (key.hashCode() * 31) + value.hashCode()
        override fun equals(other: Any?): Boolean = when (other) {
            !is BinaryHeap.Entry<*, *> -> false
            else -> this.key == other.key && this.value == other.value
        }

        override fun toString(): String = "$key -> $value"
    }

    private val _elements = mutableListOf>()

    override val size: Int get() = this._elements.size
    override val peekRoot: V?
        get() = when (this._elements.size) {
            0 -> null
            else -> this._elements[0].value
        }

    override val entries: List> get() = _elements

    override operator fun set(key: K, value: V) = this.insert(key, value)
    override fun get(key: K): List = this.peekAll(key)

    override fun isEmpty(): Boolean = 0 == this.size
    override fun isNotEmpty(): Boolean = 0 != this.size

    override fun insert(key: K, value: V) {
        val e = Entry(key, value)
        this._elements.add(e)
        this.upHeap(this._elements.size - 1, key)
    }

    override fun extractRoot(): V? {
        return if (0 == this.size) {
            null
        } else {
            this.swap(0, this._elements.size - 1)
            val oldRoot = this._elements.removeLastOrNull()
            this.downHeap(0)
            oldRoot?.value
        }
    }

    override fun extractRootAndThenInsert(key: K, value: V): V? {
        return when (this.size) {
            0 -> {
                this._elements.add(Entry(key, value))
                null
            }
            1 -> {
                val oldRoot = this._elements[0]
                this._elements[0] = Entry(key, value)
                oldRoot.value
            }
            else -> {
                this._elements.add(Entry(key, value))
                this.swap(0, this._elements.size - 1)
                val oldRoot = this._elements.removeLastOrNull()
                this.downHeap(0)
                oldRoot?.value
            }
        }
    }

    override fun insertAndThenExtractRoot(key: K, value: V): V {
        return when {
            0 == this.size -> value
            0 < this.comparator.compare(key, this._elements[0].key) -> value
            else -> {
                val oldRoot = this._elements[0]
                this._elements[0] = Entry(key, value)
                this.downHeap(0)
                oldRoot.value
            }
        }
    }

    override fun remove(key: K): V? {
        return when {
            0 == this.size -> null
            key == this._elements[0].key -> this.extractRoot()
            key == this._elements.last().key -> this._elements.removeAt(this._elements.size-1).value
            else -> {
                val index = this._elements.indexOfFirst { it.key==key }
                if (index >=0) {
                    this.swap(index, this._elements.size - 1)
                    val old = this._elements.removeAt(this._elements.size - 1)
                    this.downHeap(index)
                    old.value
                } else {
                    null
                }
            }
        }
    }

    override fun peekOneOf(key: K): V? = searchSubTreeFor(0, key).firstOrNull()

    override fun peekAll(key: K): List = searchSubTreeFor(0, key)

    override fun clear() {
        this._elements.clear()
    }

    private fun parentIndexOf(childIndex: Int) = (childIndex - 1) / 2
    private fun leftChildIndexOf(parentIndex: Int) = (2 * parentIndex) + 1
    private fun rightChildIndexOf(parentIndex: Int) = (2 * parentIndex) + 2

    private fun searchSubTreeFor(startEntryIndex: Int, key: K): List {
        val elements = mutableListOf()
        val left = leftChildIndexOf(startEntryIndex)
        val right = rightChildIndexOf(startEntryIndex)
        return when {
            startEntryIndex >= this._elements.size -> elements
            key == this._elements[startEntryIndex].key -> elements + this._elements[startEntryIndex].value + searchSubTreeFor(left, key) + searchSubTreeFor(right, key)
            0 < this.comparator.compare(key, this._elements[startEntryIndex].key) -> elements
            else -> elements + searchSubTreeFor(left, key) + searchSubTreeFor(right, key)
        }
    }

    // index - of the element to sort
    // elementKey  - of the element to sort (saves fetching it)
    // return new index of element
    private fun upHeap(index: Int, elementKey: K): Int {
        //TODO: no need to compare with self
        return when {
            1 == this.size -> index
            else -> {
                var elementIndex = index
                var parentIndex = parentIndexOf(elementIndex)
                var parentKey = this._elements[parentIndex].key
                while (parentKey != elementKey && 0 > this.comparator.compare(parentKey, elementKey)) {
                    swap(parentIndex, elementIndex)
                    elementIndex = parentIndex
                    parentIndex = parentIndexOf(elementIndex)
                    parentKey = this._elements[parentIndex].key
                }
                elementIndex
            }
        }
    }

    // index - of the element to sort
    // elementKey  - of the element to sort (saves fetching it)
    // return new index of element
    private fun downHeap(index: Int): Int {
        val leftChildIndex = leftChildIndexOf(index)
        val rightChildIndex = rightChildIndexOf(index)
        var smallest = index

        if (leftChildIndex < this._elements.size && 0 < this.comparator.compare(this._elements[leftChildIndex].key, this._elements[smallest].key)) {
            smallest = leftChildIndex
        }
        if (rightChildIndex < this._elements.size && 0 < this.comparator.compare(this._elements[rightChildIndex].key, this._elements[smallest].key)) {
            smallest = rightChildIndex
        }

        return if (smallest != index) {
            swap(index, smallest)
            downHeap(smallest)
        } else {
            index
        }
    }

    fun swap(i1: Int, i2: Int) {
        val t = this._elements[i1]
        this._elements[i1] = this._elements[i2]
        this._elements[i2] = t
    }

    // --- Iterable ---
    override fun iterator(): Iterator = object : Iterator {
        private var _sorted = this@BinaryHeapComparable._elements.sortedWith { a, b -> [email protected](b.key, a.key) }
        private var _nextIndex = 0
        override fun hasNext(): Boolean = _nextIndex < _sorted.size
        override fun next(): V = _sorted[_nextIndex].value.also { _nextIndex++ }
    }


    override fun toString(): String = when (this.size) {
        0 -> "{}"
        else -> this._elements.joinToString(separator = "\n") { it.toString() }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy