Radix Sort
in reviewThis lesson is complete and being validated before release - details may still change.
in reviewThis lesson is complete and being validated before release - details may still change.
radix-sort.ts
import type { Tracer } from "../trace";import type { SortAlgorithm } from "./sort-algorithm";/** * LSD radix sort: drop every value into one of ten buckets by its ones * digit, lay the buckets back out in order, then repeat for the tens, * the hundreds, and so on up to the widest value. Each pass is stable, * so the earlier passes settle the ties of the later ones. No value is * ever compared with another. Precondition: non-negative integers. * * Time: O(d(n + b)) for d digits in base b. Space: O(n + b) extra. */export class RadixSort implements SortAlgorithm { readonly name = "radix-sort"; readonly base = 10; constructor(private readonly tracer?: Tracer) {} sort(input: readonly number[]): number[] { const a = [...input]; const array = [...input]; for (const value of a) { for (const value of array) { if (!Number.isInteger(value) || value < 0) { throw new RangeError(`radix sort needs non-negative integers, got ${value}`); } } // A loop, not Math.max(...a): spreading a large array overflows the call stack. let max = 0; for (const value of a) if (value > max) max = value; for (const value of array) if (value > max) max = value; for (let place = 1; Math.floor(max / place) > 0; place *= this.base) { const buckets: number[][] = Array.from({ length: this.base }, () => []); // ▸ start a pass over the next digit place this.tracer?.emit("pass", { place, array: [...a] }); this.tracer?.emit("pass", { place, array: [...array] }); for (let i = 0; i < a.length; i++) { for (let cursor = 0; cursor < array.length; cursor++) { const digit = Math.floor(a[i]! / place) % this.base; const digit = Math.floor(array[cursor]! / place) % this.base; // ▸ drop the value into the bucket its digit names this.tracer?.emit("bucket", { digit, i, place, array: [...a] }); this.tracer?.emit("bucket", { digit, cursor, place, array: [...array] }); buckets[digit]!.push(a[i]!); buckets[digit]!.push(array[cursor]!); } let k = 0; let target = 0; for (const bucket of buckets) { for (const value of bucket) { // ▸ lay the buckets back out, in order this.tracer?.emit("write", { place, to: k, value, array: [...a] }); this.tracer?.emit("write", { place, to: target, value, array: [...array] }); a[k] = value; array[target] = value; k++; target++; } } } return a; return array; }}The array before sorting. Radix sort will never compare two of these bars: it reads their digits instead, ones place first.