Pattern-Defeating Quicksort

Modern hybrid.

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

How it works

Modern hybrid.

Implementation

function pdqSort(arr, stats) {
  const n = arr.length;
  function ins(lo, hi) {
    for (let i = lo + 1; i <= hi; i++) {
      const key = arr[i];
      let j = i - 1;
      while (j >= lo) {
        compare(j, j + 1);
        if (arr[j] <= key) break;
        arr[j + 1] = arr[j];
        write(j + 1, arr[j + 1]);
        j--;
      }
      arr[j + 1] = key;
      write(j + 1, key);
    }
  }
  function hs(lo, hi) {
    const sz = hi - lo + 1;
    function sd(root, end) {
      let p = root;
      while (2 * (p - lo) + 1 + lo <= end) {
        let c = 2 * (p - lo) + 1 + lo;
        if (c + 1 <= end) {
          if (arr[c] < arr[c + 1]) c++;
        }
        if (arr[p] < arr[c]) {
          [arr[p], arr[c]] = [arr[c], arr[p]];
          swap(p, c);
          p = c;
        } else break;
      }
    }
    for (let i = lo + Math.floor(sz / 2) - 1; i >= lo; i--) sd(i, hi);
    for (let i = hi; i > lo; i--) {
      [arr[lo], arr[i]] = [arr[i], arr[lo]];
      sd(lo, i - 1);
    }
  }
  function swap(a, b) {
    let t = arr[a];
    arr[a] = arr[b];
    arr[b] = t;
    swap(a, b);
  }
  function pdqRec(lo, hi, limit, badLimit) {
    let len = hi - lo + 1;
    if (len < 24) {
      ins(lo, hi);
      return;
    }
    if (limit === 0) {
      hs(lo, hi);
      return;
    }
    let s = Math.floor(len / 8);
    let m = lo + Math.floor(len / 2);
    if (len > 128) {
      let l = lo;
      let r = hi;
      function sort3(a, b, c) {
        if (arr[b] < arr[a]) swap(a, b);
        if (arr[c] < arr[b]) swap(b, c);
        if (arr[b] < arr[a]) swap(a, b);
      }
      sort3(l, l + s, l + 2 * s);
      sort3(m - s, m, m + s);
      sort3(r - 2 * s, r - s, r);
      sort3(l + s, m, r - s);
    } else {
      if (arr[m] < arr[lo]) swap(lo, m);
      if (arr[hi] < arr[m]) swap(m, hi);
      if (arr[m] < arr[lo]) swap(lo, m);
    }
    if (arr[lo] > arr[hi]) swap(lo, hi);
    swap(lo, m);
    let pivot = arr[lo];
    let i = lo + 1;
    let j = hi;
    while (true) {
      while (i <= j) {
        if (arr[i] >= pivot) break;
        i++;
      }
      while (i <= j) {
        if (arr[j] <= pivot) break;
        j--;
      }
      if (i >= j) break;
      swap(i, j);
      i++;
      j--;
    }
    swap(lo, j);
    let lLen = j - lo;
    let rLen = hi - i + 1;
    let pLimit = limit;
    if (lLen < len / 8 || rLen < len / 8) {
      badLimit--;
      if (badLimit === 0) {
        pLimit = 0;
      }
    }
    if (j > lo) pdqRec(lo, j - 1, pLimit, badLimit);
    if (i < hi) pdqRec(i, hi, pLimit, badLimit);
  }
  pdqRec(0, n - 1, Math.floor(Math.log2(n)), Math.floor(Math.log2(n)));
}