Library Sort

Gapped insertion sort.

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

How it works

Gapped insertion sort.

Implementation

function librarySort(arr, stats) {
  const n = arr.length;
  if (n < 2) return;
  let sparseSize = 1;
  while (sparseSize < n * 2) sparseSize <<= 1;
  let sparse = new Array(sparseSize).fill(undefined);
  const ordered = [];
  const valueAt = pos => {
    return sparse[ordered[pos]];
  };
  const lowerBound = v => {
    let lo = 0, hi = ordered.length;
    while (lo < hi) {
      const mid = lo + hi >> 1;
      if (valueAt(mid) < v) lo = mid + 1; else hi = mid;
    }
    return lo;
  };
  const rebalance = () => {
    const values = ordered.map(idx => sparse[idx]);
    sparseSize <<= 1;
    sparse = new Array(sparseSize).fill(undefined);
    ordered.length = 0;
    const gap = Math.max(2, Math.floor(sparseSize / Math.max(1, values.length + 1)));
    let cursor = gap;
    for (let i = 0; i < values.length; i++) {
      const v = values[i];
      if (cursor >= sparseSize) cursor = sparseSize - 1;
      sparse[cursor] = v;
      ordered.push(cursor);
      cursor += gap;
    }
  };
  const insertValue = v => {
    while (true) {
      if (ordered.length === 0) {
        const mid = Math.floor(sparseSize / 2);
        sparse[mid] = v;
        ordered.push(mid);
        return;
      }
      const pos = lowerBound(v);
      const left = pos > 0 ? ordered[pos - 1] : -1;
      const right = pos < ordered.length ? ordered[pos] : sparseSize;
      const target = Math.floor((left + right) / 2);
      let gapIdx = -1;
      for (let d = 0; d < sparseSize; d++) {
        const a = target - d;
        const b = target + d;
        if (a >= 0 && sparse[a] === undefined) {
          gapIdx = a;
          break;
        }
        if (b < sparseSize && sparse[b] === undefined) {
          gapIdx = b;
          break;
        }
      }
      if (gapIdx === -1) {
        rebalance();
        continue;
      }
      if (gapIdx < target) {
        for (let i = gapIdx; i < target; i++) {
          sparse[i] = sparse[i + 1];
        }
        for (let i = 0; i < ordered.length; i++) {
          if (ordered[i] > gapIdx && ordered[i] <= target) ordered[i]--;
        }
      } else if (gapIdx > target) {
        for (let i = gapIdx; i > target; i--) {
          sparse[i] = sparse[i - 1];
        }
        for (let i = 0; i < ordered.length; i++) {
          if (ordered[i] >= target && ordered[i] < gapIdx) ordered[i]++;
        }
      }
      sparse[target] = v;
      ordered.splice(pos, 0, target);
      return;
    }
  };
  for (let i = 0; i < n; i++) {
    insertValue(arr[i]);
    compare(i, i);
  }
  for (let i = 0; i < n; i++) {
    arr[i] = sparse[ordered[i]];
    write(i, arr[i]);
    markSorted(i);
  }
}