Cartesian Tree Sort

Min priority queue sort.

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

How it works

Min priority queue sort.

Implementation

function cartesianSort(arr, stats) {
  const n = arr.length;
  if (n < 2) return;
  const values = [...arr];
  const parent = new Array(n).fill(-1);
  const left = new Array(n).fill(-1);
  const right = new Array(n).fill(-1);
  const stack = [];
  for (let i = 0; i < n; i++) {
    let lastPopped = -1;
    while (stack.length) {
      const top = stack[stack.length - 1];
      compare(top, i);
      if (values[top] <= values[i]) break;
      lastPopped = stack.pop();
    }
    if (stack.length) {
      const top = stack[stack.length - 1];
      right[top] = i;
      parent[i] = top;
    }
    left[i] = lastPopped;
    if (lastPopped !== -1) parent[lastPopped] = i;
    stack.push(i);
  }
  const root = stack[0];
  const frontier = [];
  const compareNodes = (leftNode, rightNode) => {
    if (values[leftNode] !== values[rightNode]) return values[leftNode] - values[rightNode];
    return leftNode - rightNode;
  };
  const siftUp = index => {
    let child = index;
    while (child > 0) {
      const parentIndex = child - 1 >> 1;
      if (compareNodes(frontier[child], frontier[parentIndex]) >= 0) break;
      const temp = frontier[child];
      frontier[child] = frontier[parentIndex];
      frontier[parentIndex] = temp;
      child = parentIndex;
    }
  };
  const siftDown = index => {
    let parentIndex = index;
    while (true) {
      const leftIndex = parentIndex * 2 + 1;
      const rightIndex = leftIndex + 1;
      let smallest = parentIndex;
      if (leftIndex < frontier.length && compareNodes(frontier[leftIndex], frontier[smallest]) < 0) {
        smallest = leftIndex;
      }
      if (rightIndex < frontier.length && compareNodes(frontier[rightIndex], frontier[smallest]) < 0) {
        smallest = rightIndex;
      }
      if (smallest === parentIndex) break;
      const temp = frontier[parentIndex];
      frontier[parentIndex] = frontier[smallest];
      frontier[smallest] = temp;
      parentIndex = smallest;
    }
  };
  const pushNode = node => {
    if (node === -1) return;
    frontier.push(node);
    siftUp(frontier.length - 1);
  };
  const popNode = () => {
    const best = frontier[0];
    const tail = frontier.pop();
    if (frontier.length) {
      frontier[0] = tail;
      siftDown(0);
    }
    return best;
  };
  pushNode(root);
  for (let out = 0; out < n; out++) {
    const node = popNode();
    arr[out] = values[node];
    write(out, arr[out]);
    pushNode(left[node]);
    pushNode(right[node]);
  }
}