std.SinglyLinkedList: add sort function

This commit is contained in:
Justus Klausecker 2023-11-25 00:49:12 +01:00 committed by GitHub
parent a277181c66
commit 8b10970836
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -123,9 +123,76 @@ pub fn SinglyLinkedList(comptime T: type) type {
return 0;
}
}
/// Performs a stable in-place merge sort on the entire list.
/// This operation is O(N log N) with O(1) memory (no allocator required).
/// Sorts in ascending order with respect to the given `lessThan` function.
/// Arguments:
/// lessThanFn: Comparison function for the data of two nodes.
pub fn sort(
list: *Self,
comptime lessThanFn: fn (left: Node.Data, right: Node.Data) bool,
) void {
list.first = mergeSort(list.first, lessThanFn);
}
/// Performs a stable in-place merge sort on the given node and its descendants.
/// This operation is O(N log N) with O(1) memory (no allocator required).
/// Sorts in ascending order with respect to the given `lessThan` function.
/// Arguments:
/// node: Pointer to the first node of the list to sort.
/// lessThanFn: Comparison function for the data of two nodes.
/// Returns:
/// A pointer to the first node of the sorted list.
pub fn mergeSort(
node: ?*Node,
comptime lessThanFn: fn (left: Node.Data, right: Node.Data) bool,
) ?*Node {
if (node == null or node.?.next == null) return node;
// find middle of list
var slow = node;
var fast = node;
while (fast.?.next != null and fast.?.next.?.next != null) {
slow = slow.?.next;
fast = fast.?.next.?.next;
}
// split list in half
const half = slow.?.next;
slow.?.next = null;
// sort both halfs
const left = mergeSort(node, lessThanFn);
const right = mergeSort(half, lessThanFn);
// merge sorted halfs
return merge(left, right, lessThanFn);
}
fn merge(
left: ?*Node,
right: ?*Node,
comptime lessThanFn: fn (left: Node.Data, right: Node.Data) bool,
) ?*Node {
var left_ptr = left orelse return right;
var right_ptr = right orelse return left;
if (lessThanFn(left_ptr.data, right_ptr.data)) {
left_ptr.next = merge(left_ptr.next, right_ptr, lessThanFn);
return left_ptr;
} else {
right_ptr.next = merge(left_ptr, right_ptr.next, lessThanFn);
return right_ptr;
}
}
};
}
fn testLessThan(left: u32, right: u32) bool {
return left < right;
}
test "basic SinglyLinkedList test" {
const L = SinglyLinkedList(u32);
var list = L{};
@ -169,6 +236,22 @@ test "basic SinglyLinkedList test" {
try testing.expect(list.first.?.data == 4);
try testing.expect(list.first.?.next.?.data == 2);
try testing.expect(list.first.?.next.?.next == null);
list.prepend(&five); // {5, 4, 2}
list.prepend(&one); // {1, 5, 4, 2}
list.prepend(&three); // {3, 1, 5, 4, 2}
list.sort(testLessThan);
// Traverse forwards.
{
var it = list.first;
var index: u32 = 1;
while (it) |node| : (it = node.next) {
try testing.expect(node.data == index);
index += 1;
}
}
}
/// A doubly-linked list has a pair of pointers to both the head and