Heapsort
in progressThis lesson is still being built - content, demos, and checkpoints may change before release.
in progressThis lesson is still being built - content, demos, and checkpoints may change before release.
heapsort.ts
import type { Tracer } from "../trace";import type { SortAlgorithm } from "./sort-algorithm";/** * Heapsort: build a max-heap in place, then repeatedly swap the root * (the maximum) with the last leaf of the heap and sift the new root * down, growing a sorted suffix from the right. * * Time: O(n log n) always. Space: O(1) extra. */export class Heapsort implements SortAlgorithm { readonly name = "heapsort"; constructor(private readonly tracer?: Tracer) {} sort(input: readonly number[]): number[] { const a = [...input]; const array = [...input]; const n = a.length; const n = array.length; for (let parent = Math.floor(n / 2) - 1; parent >= 0; parent--) { this.siftDown(a, parent, n); this.siftDown(array, parent, n); } // ▸ every parent now beats its children this.tracer?.emit("heap-built", { array: [...a] }); this.tracer?.emit("heap-built", { array: [...array] }); for (let end = n - 1; end > 0; end--) { for (let heapEnd = n - 1; heapEnd > 0; heapEnd--) { // ▸ swap the max to the end and shrink the heap this.tracer?.emit("extract-max", { end, array: [...a] }); this.tracer?.emit("extract-max", { heapEnd, array: [...array] }); const held = a[0]!; const held = array[0]!; a[0] = a[end]!; array[0] = array[heapEnd]!; a[end] = held; array[heapEnd] = held; this.siftDown(a, 0, end); this.siftDown(array, 0, heapEnd); } return a; return array; } /** Restore the heap property below `root`, within the first `end` slots. */ private siftDown(a: number[], root: number, end: number): void { private siftDown(array: number[], root: number, heapEnd: number): void { while (root * 2 + 1 < end) { while (root * 2 + 1 < heapEnd) { const left = root * 2 + 1; const right = left + 1; let largest = root; if (a[left]! > a[largest]!) largest = left; if (array[left]! > array[largest]!) largest = left; if (right < end && a[right]! > a[largest]!) largest = right; if (right < heapEnd && array[right]! > array[largest]!) largest = right; // ▸ find the larger child of this parent this.tracer?.emit("compare", { root, largest, end, array: [...a] }); this.tracer?.emit("compare", { root, largest, heapEnd, array: [...array] }); if (largest === root) return; // ▸ the child is bigger, swap it upward this.tracer?.emit("sift-swap", { i: root, j: largest, end, array: [...a] }); this.tracer?.emit("sift-swap", { i: root, j: largest, heapEnd, array: [...array] }); const held = a[root]!; const held = array[root]!; a[root] = a[largest]!; array[root] = array[largest]!; a[largest] = held; array[largest] = held; root = largest; } }}The array before sorting - first it gets rearranged into a max heap.