mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-29 14:38:21 +00:00
332 lines
8.5 KiB
Zig
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);
|
|
}
|