Files
Zag/src/rtl/pairing_heap.zig
T
Loïc Grégoire 9e065ee023 initial commit
2026-03-21 22:47:06 -04:00

332 lines
8.5 KiB
Zig

//! Intrusive Pairing Heap implementation.
const std = @import("std");
pub const Node = struct {
child: ?*Node = null,
next: ?*Node = null,
prev: ?*Node = null,
};
pub fn PairingHeap(comptime order: enum { min, max }, comptime cmp: fn (*Node, *Node) std.math.Order) type {
return struct {
const Self = @This();
/// Number of elements in the heap
size: usize = 0,
/// Topmost element of the heap
root: ?*Node = null,
fn meld(a: *Node, b: *Node) *Node {
const a_wins = switch (order) {
.min => cmp(a, b) != .gt, // a <= b
.max => cmp(a, b) == .gt, // a > b
};
if (a_wins) {
if (a.child) |c| {
c.prev = b;
}
b.next = a.child;
a.child = b;
b.prev = a;
return a;
}
if (b.child) |c| {
c.prev = a;
}
a.prev = b;
a.next = b.child;
b.child = a;
return b;
}
fn merge_pairs(node: *Node) *Node {
// First pass: pair up siblings left to right
var list: ?*Node = node;
var pairs: ?*Node = null;
while (list) |a| {
const b = a.next orelse {
a.next = pairs;
a.prev = null;
pairs = a;
break;
};
list = b.next;
a.next = null;
a.prev = null;
b.next = null;
b.prev = null;
const merged = meld(a, b);
merged.next = pairs;
merged.prev = null;
pairs = merged;
}
// Second pass: fold pairs right to left
var result = pairs.?;
var rest = result.next;
result.next = null;
while (rest) |p| {
rest = p.next;
p.next = null;
result = meld(result, p);
}
return result;
}
pub fn init() Self {
return .{
.size = 0,
.root = null,
};
}
/// Insert `node` into the heap.
pub fn insert(self: *Self, node: *Node) void {
node.* = .{
.child = null,
.next = null,
.prev = null,
};
self.size += 1;
if (self.root == null) {
self.root = node;
return;
}
self.root = meld(self.root.?, node);
}
/// Return and remove the topmost element.
pub fn pop(self: *Self) ?*Node {
const root = self.root orelse return null;
const child = root.child;
root.child = null;
self.size -= 1;
if (child) |c| {
c.prev = null;
self.root = merge_pairs(c);
} else {
self.root = null;
}
return root;
}
/// Remove `node` from the heap.
pub fn remove(self: *Self, node: *Node) void {
if (self.root == node) {
_ = self.pop();
return;
}
std.debug.assert(self.root != null);
self.size -= 1;
// Unlink the node
if (node.next) |n| {
n.prev = node.prev;
}
if (node.prev) |p| {
if (p.child == node) {
// node was leftmost child
p.child = node.next;
} else {
// node was a sibling
p.next = node.next;
}
}
node.prev = null;
node.next = null;
// Merge node's children back into the heap
if (node.child) |c| {
c.prev = null;
self.root = meld(self.root.?, merge_pairs(c));
}
node.child = null;
}
};
}
const TestNode = struct {
value: u32,
node: Node = .{},
};
fn my_cmp(a: *Node, b: *Node) std.math.Order {
const ta: *TestNode = @fieldParentPtr("node", a);
const tb: *TestNode = @fieldParentPtr("node", b);
return std.math.order(ta.value, tb.value);
}
const MinHeap = PairingHeap(.min, my_cmp);
const MaxHeap = PairingHeap(.max, my_cmp);
test "empty heap" {
var heap = MinHeap.init();
try std.testing.expect(heap.root == null);
try std.testing.expectEqual(0, heap.size);
try std.testing.expect(heap.pop() == null);
}
test "single element insert and pop" {
var heap = MinHeap.init();
var a = TestNode{ .value = 42 };
heap.insert(&a.node);
try std.testing.expectEqual(1, heap.size);
const top: *TestNode = @fieldParentPtr("node", heap.root.?);
try std.testing.expectEqual(42, top.value);
_ = heap.pop();
try std.testing.expect(heap.root == null);
try std.testing.expectEqual(0, heap.size);
try std.testing.expect(heap.pop() == null);
}
test "min heap ordering" {
var heap = MinHeap.init();
var a = TestNode{ .value = 3 };
var b = TestNode{ .value = 1 };
var c = TestNode{ .value = 2 };
heap.insert(&a.node);
heap.insert(&b.node);
heap.insert(&c.node);
try std.testing.expectEqual(3, heap.size);
const first: *TestNode = @fieldParentPtr("node", heap.pop().?);
try std.testing.expectEqual(1, first.value);
const second: *TestNode = @fieldParentPtr("node", heap.pop().?);
try std.testing.expectEqual(2, second.value);
const third: *TestNode = @fieldParentPtr("node", heap.pop().?);
try std.testing.expectEqual(3, third.value);
try std.testing.expect(heap.pop() == null);
}
test "max heap ordering" {
var heap = MaxHeap.init();
var a = TestNode{ .value = 3 };
var b = TestNode{ .value = 1 };
var c = TestNode{ .value = 2 };
heap.insert(&a.node);
heap.insert(&b.node);
heap.insert(&c.node);
const first: *TestNode = @fieldParentPtr("node", heap.pop().?);
try std.testing.expectEqual(3, first.value);
const second: *TestNode = @fieldParentPtr("node", heap.pop().?);
try std.testing.expectEqual(2, second.value);
const third: *TestNode = @fieldParentPtr("node", heap.pop().?);
try std.testing.expectEqual(1, third.value);
}
test "remove middle node" {
var heap = MinHeap.init();
var a = TestNode{ .value = 1 };
var b = TestNode{ .value = 2 };
var c = TestNode{ .value = 3 };
heap.insert(&a.node);
heap.insert(&b.node);
heap.insert(&c.node);
heap.remove(&b.node);
try std.testing.expectEqual(2, heap.size);
const first: *TestNode = @fieldParentPtr("node", heap.pop().?);
try std.testing.expectEqual(1, first.value);
const second: *TestNode = @fieldParentPtr("node", heap.pop().?);
try std.testing.expectEqual(3, second.value);
try std.testing.expect(heap.pop() == null);
}
test "remove root" {
var heap = MinHeap.init();
var a = TestNode{ .value = 1 };
var b = TestNode{ .value = 2 };
var c = TestNode{ .value = 3 };
heap.insert(&a.node);
heap.insert(&b.node);
heap.insert(&c.node);
heap.remove(&a.node);
try std.testing.expectEqual(2, heap.size);
const top: *TestNode = @fieldParentPtr("node", heap.root.?);
try std.testing.expectEqual(2, top.value);
}
test "remove last element" {
var heap = MinHeap.init();
var a = TestNode{ .value = 1 };
heap.insert(&a.node);
heap.remove(&a.node);
try std.testing.expectEqual(0, heap.size);
try std.testing.expect(heap.root == null);
}
test "duplicate values" {
var heap = MinHeap.init();
var a = TestNode{ .value = 1 };
var b = TestNode{ .value = 1 };
var c = TestNode{ .value = 1 };
heap.insert(&a.node);
heap.insert(&b.node);
heap.insert(&c.node);
try std.testing.expectEqual(3, heap.size);
_ = heap.pop();
_ = heap.pop();
_ = heap.pop();
try std.testing.expectEqual(0, heap.size);
try std.testing.expect(heap.pop() == null);
}
test "large sequence" {
var heap = MinHeap.init();
var nodes: [16]TestNode = undefined;
for (&nodes, 0..) |*n, i| {
n.* = .{ .value = @intCast(16 - i) };
heap.insert(&n.node);
}
try std.testing.expectEqual(16, heap.size);
var prev: u32 = 0;
while (heap.pop()) |n| {
const t: *TestNode = @fieldParentPtr("node", n);
try std.testing.expect(t.value >= prev);
prev = t.value;
}
try std.testing.expectEqual(0, heap.size);
}