# std.sort (Zig 0.16.0) Sorting algorithms and binary search utilities. All sorts are in-place and require no allocator. ## Quick Reference | Function | Stable | Worst-case | When to Use | |----------|--------|------------|-------------| | `block` | Yes | O(n log n) | Default choice when stability matters | | `pdq` | No | O(n log n) | Default choice when stability doesn't matter | | `insertion` | Yes | O(n²) | Small arrays or nearly sorted data | | `heap` | No | O(n log n) | Guaranteed worst-case, no recursion | ## Comparator Functions All sort functions take a `lessThan` comparator: ```zig const std = @import("std"); // Simple comparator (ascending) fn lessThan(_: void, a: i32, b: i32) bool { return a < b; } // Use built-in generators for common cases const asc_i32 = std.sort.asc(i32); // ascending const desc_i32 = std.sort.desc(i32); // descending ``` ## Basic Sorting ```zig var items = [_]i32{ 5, 2, 8, 1, 9 }; // General-purpose unstable sort std.sort.pdq(i32, &items, {}, std.sort.asc(i32)); // items = [1, 2, 5, 8, 9] // Stable sort (preserves order of equal elements) std.sort.block(i32, &items, {}, std.sort.asc(i32)); // Descending order std.sort.pdq(i32, &items, {}, std.sort.desc(i32)); // items = [9, 8, 5, 2, 1] ``` ## Sorting with Context Pass external data to the comparator: ```zig const scores = [_]u32{ 50, 30, 80, 20 }; var indices = [_]usize{ 0, 1, 2, 3 }; fn compareByScore(scores_ctx: []const u32, a: usize, b: usize) bool { return scores_ctx[a] < scores_ctx[b]; } std.sort.pdq(usize, &indices, &scores, compareByScore); // indices = [3, 1, 0, 2] (sorted by score: 20, 30, 50, 80) ``` ## Sorting Structs ```zig const Person = struct { name: []const u8, age: u32, }; fn byAge(_: void, a: Person, b: Person) bool { return a.age < b.age; } var people = [_]Person{ .{ .name = "Alice", .age = 30 }, .{ .name = "Bob", .age = 25 }, .{ .name = "Carol", .age = 35 }, }; std.sort.pdq(Person, &people, {}, byAge); // Sorted: Bob (25), Alice (30), Carol (35) ``` ## Check if Sorted ```zig const items = [_]i32{ 1, 2, 3, 4, 5 }; const sorted = std.sort.isSorted(i32, &items, {}, std.sort.asc(i32)); // true ``` ## Binary Search Find element in sorted array. Comparator returns `Order` (.lt, .eq, .gt): ```zig fn order(target: i32, item: i32) std.math.Order { return std.math.order(target, item); } const items = [_]i32{ 1, 3, 5, 7, 9 }; // Find exact match const idx = std.sort.binarySearch(i32, &items, @as(i32, 5), order); // ?usize = 2 // Not found const missing = std.sort.binarySearch(i32, &items, @as(i32, 4), order); // null ``` ## Lower/Upper Bound Find insertion points for sorted arrays: ```zig fn order(target: i32, item: i32) std.math.Order { return std.math.order(target, item); } const items = [_]i32{ 1, 3, 5, 5, 5, 7, 9 }; // First position where target could be inserted (first >= target) const lower = std.sort.lowerBound(i32, &items, @as(i32, 5), order); // 2 (first 5) // First position after all equal elements (first > target) const upper = std.sort.upperBound(i32, &items, @as(i32, 5), order); // 5 (after last 5) // Both bounds at once const range = std.sort.equalRange(i32, &items, @as(i32, 5), order); // .{ 2, 5 } (indices of all 5s) ``` ## Partition Point Find where predicate changes from true to false: ```zig fn lessThan5(_: void, item: i32) bool { return item < 5; } const items = [_]i32{ 1, 2, 3, 4, 5, 6, 7 }; const point = std.sort.partitionPoint(i32, &items, {}, lessThan5); // 4 (first index where predicate is false) ``` ## Min/Max ```zig const items = [_]i32{ 5, 2, 8, 1, 9 }; // Get min/max value const minimum = std.sort.min(i32, &items, {}, std.sort.asc(i32)); // ?i32 = 1 const maximum = std.sort.max(i32, &items, {}, std.sort.asc(i32)); // ?i32 = 9 // Get index of min/max const min_idx = std.sort.argMin(i32, &items, {}, std.sort.asc(i32)); // ?usize = 3 const max_idx = std.sort.argMax(i32, &items, {}, std.sort.asc(i32)); // ?usize = 4 // Empty slice returns null const empty: []const i32 = &.{}; const none = std.sort.min(i32, empty, {}, std.sort.asc(i32)); // null ``` ## Context-Based Sorting (Advanced) For sorting indices into external data using index-based context: ```zig const Context = struct { items: []i32, pub fn lessThan(ctx: @This(), a: usize, b: usize) bool { return ctx.items[a] < ctx.items[b]; } pub fn swap(ctx: @This(), a: usize, b: usize) void { std.mem.swap(i32, &ctx.items[a], &ctx.items[b]); } }; var items = [_]i32{ 5, 2, 8, 1 }; const ctx = Context{ .items = &items }; // Sort a subrange using indices std.sort.pdqContext(1, 4, ctx); // sort end-exclusive range [1, 4): indices 1, 2, 3 // items = [5, 1, 2, 8] ``` ## Stable Sort Example When sorting by one field but preserving order of equal elements: ```zig const Item = struct { id: usize, priority: u32, }; fn byPriority(_: void, a: Item, b: Item) bool { return a.priority < b.priority; } var items = [_]Item{ .{ .id = 0, .priority = 1 }, .{ .id = 1, .priority = 1 }, // same priority as id=0 .{ .id = 2, .priority = 0 }, }; // Stable sort preserves id order for equal priorities std.sort.block(Item, &items, {}, byPriority); // Result: {id=2, p=0}, {id=0, p=1}, {id=1, p=1} // id=0 still comes before id=1 ``` ## Algorithm Selection - **`pdq`** (Pattern-Defeating Quicksort): General-purpose unstable sort. Adapts to input patterns, falls back to heapsort for worst cases. - **`block`**: General-purpose stable sort. Preserves relative order of equal elements. - **`insertion`**: O(n) on nearly sorted data. Use for small arrays or as final pass. - **`heap`**: Guaranteed O(n log n) with O(1) memory. No recursion, predictable performance. ## Notes - All sorts are **in-place** with O(1) or O(log n) auxiliary memory - Comparators must define a strict weak ordering: irreflexive and transitive, with a transitive equivalence relation for elements where neither compares less than the other. `block` is limited to less-than/greater-than style comparisons as documented by that algorithm. - `asc`/`desc` helpers work with any type supporting `<` operator - Binary search functions require the array to already be sorted - `equalRange` returns both bounds in O(log n); do not assume it is faster than calling the two bound helpers without measurement.