IntroSort

Starts as quicksort, switches to heapsort if recursion gets too deep, and insertion sort for small partitions. Used by C++ STL.

Best O(n log 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

Starts as quicksort, switches to heapsort if recursion gets too deep, and insertion sort for small partitions. Used by C++ STL.

Implementation

function introSort(arr, stats) {
  const n = arr.length;
  const sorted = new Set();
  if (n < 2) {
    if (n === 1) markSorted(0);
    return;
  }
  const maxDepth = Math.floor(2 * Math.log2(n));
  function swap(i, j) {
    if (i === j) return;
    swap(i, j, {
      sorted
    });
    const t = arr[i];
    arr[i] = arr[j];
    arr[j] = t;
  }
  function insertSort(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, {
          sorted
        });
        if (arr[j] <= key) break;
        arr[j + 1] = arr[j];
        write(j + 1, arr[j + 1], {
          sorted
        });
        j--;
      }
      arr[j + 1] = key;
      write(j + 1, key, {
        sorted
      });
    }
  }
  function siftDown(lo, root, end) {
    let parent = root;
    while (2 * (parent - lo) + 1 + lo <= end) {
      let child = 2 * (parent - lo) + 1 + lo;
      if (child + 1 <= end) {
        compare(child, child + 1, {
          sorted
        });
        if (arr[child] < arr[child + 1]) {
          child++;
        }
      }
      compare(parent, child, {
        sorted
      });
      if (arr[parent] < arr[child]) {
        swap(parent, child);
        parent = child;
      } else break;
    }
  }
  function heapRange(lo, hi) {
    const size = hi - lo + 1;
    for (let i = lo + Math.floor(size / 2) - 1; i >= lo; i--) siftDown(lo, i, hi);
    for (let i = hi; i > lo; i--) {
      swap(lo, i);
      markSorted(i);
      siftDown(lo, lo, i - 1);
    }
    markSorted(lo);
  }
  function median3(lo, mid, hi) {
    compare(mid, lo, {
      sorted
    });
    if (arr[mid] < arr[lo]) swap(lo, mid);
    compare(hi, lo, {
      sorted
    });
    if (arr[hi] < arr[lo]) swap(lo, hi);
    compare(hi, mid, {
      sorted
    });
    if (arr[hi] < arr[mid]) swap(mid, hi);
  }
  function introRec(lo, hi, depth) {
    if (hi - lo < 16) {
      insertSort(lo, hi);
      return;
    }
    if (depth === 0) {
      heapRange(lo, hi);
      return;
    }
    const mid = lo + Math.floor((hi - lo) / 2);
    median3(lo, mid, hi);
    const pivot = arr[mid];
    swap(mid, hi - 1);
    let i = lo, j = hi - 1;
    while (true) {
      do {
        i++;
        compare(i, hi - 1, {
          sorted
        });
      } while (arr[i] < pivot);
      do {
        j--;
        compare(j, hi - 1, {
          sorted
        });
      } while (arr[j] > pivot);
      if (i >= j) break;
      swap(i, j);
    }
    swap(i, hi - 1);
    markSorted(i);
    introRec(lo, i - 1, depth - 1);
    introRec(i + 1, hi, depth - 1);
    checkpoint(sorted.size, n);
  }
  introRec(0, n - 1, maxDepth);
  for (let i = 0; i < n; i++) markSorted(i);
}