Multikey Quicksort

3-way radix quicksort: partitions on one key symbol at a time (here, decimal digits from most significant to least), recursing deeper only on the equal-digit middle. Ideal for keys built from a small alphabet.

Best O(n) Avg O(n log n) Worst O(n²) Space O(log n) Stable No In-place Yes Comparison-based

How it works

3-way radix quicksort: partitions on one key symbol at a time (here, decimal digits from most significant to least), recursing deeper only on the equal-digit middle. Ideal for keys built from a small alphabet.

Implementation

function multikeyQuickSort(arr, stats) {
  const n = arr.length;
  if (n < 2) return;
  let maxVal = 0;
  for (let i = 0; i < n; i++) {
    if (arr[i] > maxVal) maxVal = arr[i];
  }
  let maxDigits = 1;
  let remaining = maxVal;
  while (remaining >= 10) {
    remaining = Math.floor(remaining / 10);
    maxDigits++;
  }
  function digitAt(v, d) {
    const power = maxDigits - 1 - d;
    let divisor = 1;
    for (let k = 0; k < power; k++) divisor = divisor * 10;
    return Math.floor(v / divisor) % 10;
  }
  function sort(lo, hi, d) {
    if (lo >= hi || d >= maxDigits) return;
    let lt = lo;
    let gt = hi;
    let i = lo + 1;
    const pivot = digitAt(arr[lo], d);
    while (i <= gt) {
      const code = digitAt(arr[i], d);
      compare(i, lo);
      if (code < pivot) {
        [arr[lt], arr[i]] = [arr[i], arr[lt]];
        swap(lt, i);
        lt++;
        i++;
      } else if (code > pivot) {
        [arr[i], arr[gt]] = [arr[gt], arr[i]];
        swap(i, gt);
        gt--;
      } else {
        i++;
      }
    }
    sort(lo, lt - 1, d);
    sort(lt, gt, d + 1);
    sort(gt + 1, hi, d);
  }
  sort(0, n - 1, 0);
  for (let i = 0; i < n; i++) markSorted(i);
}