Skip to content

Instantly share code, notes, and snippets.

@jnorthrup
Created September 26, 2024 22:33
Show Gist options
  • Save jnorthrup/0cb7a00087979cd4341b6242847919f4 to your computer and use it in GitHub Desktop.
Save jnorthrup/0cb7a00087979cd4341b6242847919f4 to your computer and use it in GitHub Desktop.
class StrictFibonacciHeap<T : Comparable<T>> {
private var root: Node<T>? = null
private var minNode: Node<T>? = null
private var size = 0
private val activeRoots = mutableListOf<Node<T>>()
private val rankList = mutableListOf<MutableList<Node<T>>>()
private class Node<T : Comparable<T>>(
var key: T,
var value: Any? = null,
var parent: Node<T>? = null,
var child: Node<T>? = null,
var left: Node<T>? = null,
var right: Node<T>? = null,
var rank: Int = 0,
var loss: Int = 0,
var isActive: Boolean = true
)
fun insert(key: T, value: Any? = null): Node<T> {
val newNode = Node(key, value)
if (root == null) {
root = newNode
minNode = newNode
} else {
link(newNode, root!!)
if (newNode.key < minNode!!.key) {
minNode = newNode
}
}
size++
activeRoots.add(newNode)
maintainInvariants()
return newNode
}
fun findMin(): T? = minNode?.key
fun deleteMin(): T? {
val min = minNode ?: return null
if (min.child != null) {
var child = min.child
do {
val next = child!!.right
child.parent = null
link(child, root!!)
activeRoots.add(child)
child = next
} while (child != min.child)
}
if (min == root) {
root = min.right
}
unlink(min)
activeRoots.remove(min)
size--
if (size == 0) {
minNode = null
root = null
} else {
minNode = root
consolidate()
}
maintainInvariants()
return min.key
}
fun decreaseKey(node: Node<T>, newKey: T) {
require(newKey < node.key) { "New key must be smaller than current key" }
node.key = newKey
if (node.parent != null && node.key < node.parent!!.key) {
cut(node)
cascadingCut(node.parent!!)
}
if (node.key < minNode!!.key) {
minNode = node
}
maintainInvariants()
}
private fun link(child: Node<T>, parent: Node<T>) {
child.parent = parent
if (parent.child == null) {
parent.child = child
child.left = child
child.right = child
} else {
child.left = parent.child!!.left
child.right = parent.child
parent.child!!.left!!.right = child
parent.child!!.left = child
}
parent.rank++
}
private fun unlink(node: Node<T>) {
if (node.right == node) {
node.parent?.child = null
} else {
node.left!!.right = node.right
node.right!!.left = node.left
node.parent?.child = node.right
}
node.parent?.rank?.dec()
node.parent = null
node.left = null
node.right = null
}
private fun cut(node: Node<T>) {
unlink(node)
link(node, root!!)
node.loss = 0
node.isActive = true
activeRoots.add(node)
}
private fun cascadingCut(node: Node<T>) {
val parent = node.parent
if (parent != null) {
if (node.isActive) {
node.loss++
if (node.loss > 1) {
cut(node)
cascadingCut(parent)
}
} else {
node.isActive = true
node.loss = 0
}
}
}
private fun consolidate() {
val maxRank = (log2(size.toDouble()) + 1).toInt()
val ranks = Array<Node<T>?>(maxRank) { null }
var x = root
val roots = mutableListOf<Node<T>>()
while (x != null) {
roots.add(x)
x = x.right
if (x == root) break
}
for (node in roots) {
var r = node.rank
while (ranks[r] != null) {
val y = ranks[r]!!
if (node.key > y.key) {
val temp = node
node = y
y = temp
}
link(y, node)
ranks[r] = null
r++
}
ranks[r] = node
if (node.key < minNode!!.key) {
minNode = node
}
}
root = minNode
}
private fun maintainInvariants() {
while (activeRoots.size > 2 * log2(size.toDouble()) + 6) {
val x = activeRoots.removeAt(activeRoots.lastIndex)
val y = activeRoots.removeAt(activeRoots.lastIndex)
if (x.key < y.key) {
link(y, x)
activeRoots.add(x)
} else {
link(x, y)
activeRoots.add(y)
}
}
}
fun meld(other: StrictFibonacciHeap<T>) {
if (other.root == null) return
if (root == null) {
root = other.root
minNode = other.minNode
size = other.size
activeRoots.addAll(other.activeRoots)
} else {
val last = root!!.left!!
val otherLast = other.root!!.left!!
last.right = other.root
other.root!!.left = last
root!!.left = otherLast
otherLast.right = root
if (other.minNode!!.key < minNode!!.key) {
minNode = other.minNode
}
size += other.size
activeRoots.addAll(other.activeRoots)
}
maintainInvariants()
}
fun delete(node: Node<T>) {
decreaseKey(node, minNode!!.key)
deleteMin()
}
private fun log2(n: Double): Int = (ln(n) / ln(2.0)).toInt()
}
@jnorthrup
Copy link
Author

jnorthrup commented Sep 26, 2024

Here's an idiomatic implementation of Strict Fibonacci Heaps in Kotlin:

This implementation of Strict Fibonacci Heaps in Kotlin provides the following operations:

  1. insert(key: T, value: Any? = null): Inserts a new element with the given key and optional value.
  2. findMin(): Returns the minimum key in the heap.
  3. deleteMin(): Removes and returns the minimum key from the heap.
  4. decreaseKey(node: Node<T>, newKey: T): Decreases the key of a given node.
  5. meld(other: StrictFibonacciHeap<T>): Merges another Strict Fibonacci Heap into this one.
  6. delete(node: Node<T>): Deletes a specific node from the heap.

The implementation uses a node-based structure with parent, child, and sibling pointers. It maintains invariants such as the maximum number of active roots and the loss of each node.

Key points of this implementation:

  1. It uses a generic type T that extends Comparable<T> to ensure keys can be compared.
  2. The Node class is private and contains all necessary fields for the heap structure.
  3. The maintainInvariants() method ensures that the number of active roots stays within the required bounds.
  4. The consolidate() method is used after deleteMin() to reorganize the heap structure.
  5. The meld() operation combines two heaps efficiently.

This implementation aims to provide O(1) amortized time for insert, find-min, and decrease-key operations, and O(log n) amortized time for delete-min operations, where n is the number of elements in the heap.

Note that this implementation focuses on correctness and readability rather than optimizing for all possible edge cases. In a production environment, you might want to add more error checking and potentially optimize some operations further.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment