mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-31 15:28:21 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
//! 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);
|
||||
}
|
||||
Reference in New Issue
Block a user