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

dorkbox.collections.BinaryHeap.kt Maven / Gradle / Ivy

/*
 * Copyright 2023 dorkbox, llc
 *
 * 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.
 */
/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 *
 * 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 dorkbox.collections

import dorkbox.collections.BinaryHeap.Node
import java.util.*

/** A binary heap that stores nodes which each have a float value and are sorted either lowest first or highest first. The
 * [Node] class can be extended to store additional information.
 * @author Nathan Sweet
 */
class BinaryHeap @JvmOverloads constructor(capacity: Int = 16, private val isMaxHeap: Boolean = false) {
    companion object {
        const val version = Collections.version
    }

    var size = 0
    private var nodes: Array

    init {
        nodes = arrayOfNulls(capacity)
    }

    /**
     * Adds the node to the heap using its current value. The node should not already be in the heap.
     * @return The specified node.
     */
    fun add(node: T): T {
        // Expand if necessary.
        if (size == nodes.size) {
            val newNodes = arrayOfNulls(size shl 1)
            System.arraycopy(nodes, 0, newNodes, 0, size)
            nodes = newNodes
        }
        // Insert at end and bubble up.
        node!!.index = size
        nodes[size] = node
        up(size++)
        return node
    }

    /** Sets the node's value and adds it to the heap. The node should not already be in the heap.
     * @return The specified node.
     */
    fun add(node: T, value: Float): T {
        node!!.value = value
        return add(node)
    }

    /** Returns true if the heap contains the specified node.
     * @param identity If true, == comparison will be used. If false, .equals() comparison will be used.
     */
    fun contains(node: T?, identity: Boolean): Boolean {
        requireNotNull(node) { "node cannot be null." }
        if (identity) {
            for (n in nodes) if (n === node) return true
        }
        else {
            for (other in nodes) if (other == node) return true
        }
        return false
    }

    /** Returns the first item in the heap. This is the item with the lowest value (or highest value if this heap is configured as
     * a max heap).  */
    fun peek(): T? {
        check(size != 0) { "The heap is empty." }
        @Suppress("UNCHECKED_CAST")
        return nodes[0] as T?
    }

    /** Removes the first item in the heap and returns it. This is the item with the lowest value (or highest value if this heap is
     * configured as a max heap).  */
    fun pop(): T? {
        val removed = nodes[0]
        if (--size > 0) {
            nodes[0] = nodes[size]
            nodes[size] = null
            down(0)
        }
        else nodes[0] = null
        @Suppress("UNCHECKED_CAST")
        return removed as T?
    }

    /** @return The specified node.
     */
    fun remove(node: T): T {
        if (--size > 0) {
            val moved = nodes[size]
            nodes[size] = null
            nodes[node!!.index] = moved
            if ((moved!!.value < node.value) xor isMaxHeap) up(node.index) else down(node.index)
        }
        else nodes[0] = null
        return node
    }

    /** Returns true if the heap has one or more items.  */
    fun notEmpty(): Boolean {
        return size > 0
    }

    val isEmpty: Boolean
        /** Returns true if the heap is empty.  */
        get() = size == 0

    fun clear() {
        Arrays.fill(nodes, 0, size, null)
        size = 0
    }

    /** Changes the value of the node, which should already be in the heap.  */
    fun setValue(node: T, value: Float) {
        val oldValue = node!!.value
        node.value = value
        if ((value < oldValue) xor isMaxHeap) up(node.index) else down(node.index)
    }

    @Suppress("NAME_SHADOWING")
    private fun up(index: Int) {
        var index = index
        val nodes = nodes
        val node = nodes[index]
        val value = node!!.value
        while (index > 0) {
            val parentIndex = index - 1 shr 1
            val parent = nodes[parentIndex]
            if ((value < parent!!.value) xor isMaxHeap) {
                nodes[index] = parent
                parent.index = index
                index = parentIndex
            }
            else break
        }
        nodes[index] = node
        node.index = index
    }

    @Suppress("NAME_SHADOWING")
    private fun down(index: Int) {
        var index = index
        val nodes = nodes
        val size = size
        val node = nodes[index]
        val value = node!!.value
        while (true) {
            val leftIndex = 1 + (index shl 1)
            if (leftIndex >= size) break
            val rightIndex = leftIndex + 1

            // Always has a left child.
            val leftNode = nodes[leftIndex]
            val leftValue = leftNode!!.value

            // May have a right child.
            var rightNode: Node?
            var rightValue: Float
            if (rightIndex >= size) {
                rightNode = null
                rightValue = if (isMaxHeap) -Float.MAX_VALUE else Float.MAX_VALUE
            }
            else {
                rightNode = nodes[rightIndex]
                rightValue = rightNode!!.value
            }

            // The smallest of the three values is the parent.
            if ((leftValue < rightValue) xor isMaxHeap) {
                if (leftValue == value || (leftValue > value) xor isMaxHeap) break
                nodes[index] = leftNode
                leftNode.index = index
                index = leftIndex
            }
            else {
                if (rightValue == value || (rightValue > value) xor isMaxHeap) break
                nodes[index] = rightNode
                if (rightNode != null) rightNode.index = index
                index = rightIndex
            }
        }
        nodes[index] = node
        node.index = index
    }

    override fun equals(other: Any?): Boolean {
        if (other !is BinaryHeap<*>) return false
        if (other.size != size) return false
        val nodes1 = nodes
        val nodes2 = other.nodes
        var i = 0
        val n = size
        while (i < n) {
            if (nodes1[i]!!.value != nodes2[i]!!.value) return false
            i++
        }
        return true
    }

    override fun hashCode(): Int {
        var h = 1
        val nodes = nodes
        var i = 0
        val n = size
        while (i < n) {
            h = h * 31 + java.lang.Float.floatToIntBits(nodes[i]!!.value)
            i++
        }
        return h
    }

    override fun toString(): String {
        if (size == 0) return "[]"
        val nodes = nodes
        val buffer = StringBuilder(32)
        buffer.append('[')
        buffer.append(nodes[0]!!.value)
        for (i in 1 until size) {
            buffer.append(", ")
            buffer.append(nodes[i]!!.value)
        }
        buffer.append(']')
        return buffer.toString()
    }

    /** A binary heap node.
     * @author Nathan Sweet
     */
    open class Node
    /** @param value The initial value for the node. To change the value, use [BinaryHeap.add] if the node is
     * not in the heap, or [BinaryHeap.setValue] if the node is in the heap.
     */(var value: Float) {
        var index = 0
        override fun toString(): String {
            return value.toString()
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy