Current section
Files
Jump to
Current section
Files
src/gleamy/leftist_heap.gleam
//// This module provides an implementation of the leftist heap data structure,
//// a type of binary 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(Int, a, Tree(a), 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(log n)
pub fn insert(heap: Heap(a), item: a) -> Heap(a) {
Heap(
merge_trees(Tree(1, item, Empty, Empty), 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)
pub fn delete_min(heap: Heap(a)) -> Result(#(a, Heap(a)), Nil) {
case heap.root {
Tree(_, x, a, b) ->
Ok(#(x, Heap(merge_trees(a, b, 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(log n)
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(h1: Tree(a), h2: Tree(a), compare: fn(a, a) -> Order) -> Tree(a) {
case h1, h2 {
h, Empty -> h
Empty, h -> h
Tree(_, x, a1, b1), Tree(_, y, a2, b2) ->
case compare(x, y) {
Gt -> make(y, a2, merge_trees(h1, b2, compare))
_ -> make(x, a1, merge_trees(b1, h2, compare))
}
}
}
fn make(x, a, b) {
let rank_a = case a {
Tree(r, _, _, _) -> r
Empty -> 0
}
let rank_b = case b {
Tree(r, _, _, _) -> r
Empty -> 0
}
case rank_a < rank_b {
True -> Tree(rank_a + 1, x, b, a)
_ -> Tree(rank_b + 1, x, a, b)
}
}