Current section
Files
Jump to
Current section
Files
src/gleamy/pairing_heap.gleam
//// This module provides an implementation of the pairing heap data structure,
//// a type of self-adjusting heap with efficient insert, find_min, and delete_min, and merge operations.
// Based on "Purely Functional Data Structures" by Okasaki (1998)
import gleam/order.{type Order, Gt}
type Tree(a) {
Empty
Tree(a, List(Tree(a)))
}
pub opaque type Heap(a) {
Heap(root: Tree(a), compare: fn(a, a) -> Order)
}
/// Creates a new empty heap with the provided comparison function.
pub fn new(compare: fn(a, a) -> Order) -> Heap(a) {
Heap(Empty, compare)
}
/// Inserts a new item into the heap, preserving the heap property.
/// Time complexity: O(1)
pub fn insert(heap: Heap(a), key: a) -> Heap(a) {
Heap(merge_trees(Tree(key, []), heap.root, heap.compare), heap.compare)
}
/// Returns the minimum element in the heap, if the heap is not empty.
/// Time complexity: O(1)
pub fn find_min(heap: Heap(a)) -> Result(a, Nil) {
case heap.root {
Tree(x, _) -> Ok(x)
Empty -> Error(Nil)
}
}
/// Removes and returns the minimum element from the heap along with the
/// new heap after deletion, if the heap is not empty.
/// Time complexity: O(log n) amortized
pub fn delete_min(heap: Heap(a)) -> Result(#(a, Heap(a)), Nil) {
case heap.root {
Tree(x, xs) -> Ok(#(x, Heap(merge_pairs(xs, heap.compare), heap.compare)))
Empty -> Error(Nil)
}
}
/// Merges two heaps into a new heap containing all elements from both heaps,
/// preserving the heap property.
/// The given heaps must have the same comparison function.
/// Time complexity: O(1)
pub fn merge(heap1: Heap(a), heap2: Heap(a)) -> Heap(a) {
let compare = heap1.compare
Heap(merge_trees(heap1.root, heap2.root, compare), compare)
}
fn merge_trees(x: Tree(a), y: Tree(a), compare: fn(a, a) -> Order) -> Tree(a) {
case x, y {
x, Empty -> x
Empty, y -> y
Tree(xk, xs), Tree(yk, ys) ->
case compare(xk, yk) {
Gt -> Tree(yk, [x, ..ys])
_ -> Tree(xk, [y, ..xs])
}
}
}
fn merge_pairs(l: List(Tree(a)), compare: fn(a, a) -> Order) -> Tree(a) {
case l {
[] -> Empty
[h] -> h
[h1, h2, ..hs] ->
merge_trees(
merge_trees(h1, h2, compare),
merge_pairs(hs, compare),
compare,
)
}
}