最小堆算法

6 阅读3分钟

概念

#完全二叉树结构

最小堆是一种特殊的完全二叉树。在完全二叉树中,除了最底层之外的所有层都被完全填满,最底层的节点从左到右填充。

       10
     /    \
   15      20
  /  \
25   30

#堆的有序性

在最小堆中,任何给定节点的值都小于或等于其子节点的值。这意味着根节点总是具有最小的值。

#最小堆的操作

插入(Insertion)  插入新元素时,需要确保堆的性质保持不变。 例如,向上面的堆中插入5:

       5
     /   \
   15    10
  /  \   /
25   30 20
function insert(heap, value) {
  heap.push(value);
  let index = heap.length - 1;
  while (index > 0) {
    let parentIndex = Math.floor((index - 1) / 2);
    if (heap[parentIndex] <= heap[index]) {
      break;
    }
    [heap[parentIndex], heap[index]] = [heap[index], heap[parentIndex]];
    index = parentIndex;
  }
}

删除最小元素(Delete Min)  删除根节点并重新组织堆。

例如,从上面的堆中删除最小元素:

       10
     /    \
   15      20
  /  \
25   30
function deleteMin(heap) {
  if (heap.length === 0) {
    return null;
  }
  const minVal = heap[0];
  heap[0] = heap[heap.length - 1];
  heap.pop();
  let index = 0;
  while (true) {
    const leftChildIndex = 2 * index + 1;
    const rightChildIndex = 2 * index + 2;
    if (leftChildIndex >= heap.length) {
      break;
    }
    let minIndex = index;
    if (heap[leftChildIndex] < heap[minIndex]) {
      minIndex = leftChildIndex;
    }
    if (rightChildIndex < heap.length && heap[rightChildIndex] < heap[minIndex]) {
      minIndex = rightChildIndex;
    }
    if (minIndex === index) {
      break;
    }
    [heap[index], heap[minIndex]] = [heap[minIndex], heap[index]];
    index = minIndex;
  }
  return minVal;
}

堆化(Heapify)  将普通数组转换为最小堆。

例如,考虑数组[10, 15, 20, 25, 30],堆化后的结构为:

       10
     /    \
   15      20
  /  \
25   30
function heapify(arr) {
  const n = arr.length;
  for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
    minHeapify(arr, i, n);
  }
}

function minHeapify(arr, i, n) {
  let smallest = i;
  const l = 2 * i + 1;
  const r = 2 * i + 2;
  if (l < n && arr[l] < arr[smallest]) {
    smallest = l;
  }
  if (r < n && arr[r] < arr[smallest]) {
    smallest = r;
  }
  if (smallest !== i) {
    [arr[i], arr[smallest]] = [arr[smallest], arr[i]];
    minHeapify(arr, smallest, n);
  }
}

最小堆是一种非常实用的数据结构,适用于需要快速访问和删除最小元素的场景。

#scheduler/src/forks/SchedulerMinHeap.js

/**
 * 将节点推入堆中
 * @param {Object[]} heap - 堆
 * @param {Object} node - 要推入的节点
 */
export function push(heap, node) {
  const index = heap.length;
  heap.push(node);
  siftUp(heap, node, index);
}

/**
 * 返回堆顶元素
 * @param {Object[]} heap - 堆
 * @returns {Object|null} 堆顶元素,如果堆为空,则返回 null
 */
export function peek(heap) {
  return heap.length === 0 ? null : heap[0];
}

/**
 * 从堆中弹出顶部元素
 * @param {Object[]} heap - 堆
 * @returns {Object|null} 堆顶元素,如果堆为空,则返回 null
 */
export function pop(heap) {
  if (heap.length === 0) {
    return null;
  }
  const first = heap[0];
  const last = heap.pop();
  if (last !== first) {
    heap[0] = last;
    siftDown(heap, last, 0);
  }
  return first;
}

/**
 * 将堆中的元素向上移动以维护堆的性质
 * @param {Object[]} heap - 堆
 * @param {Object} node - 要移动的节点
 * @param {number} i - 节点在堆中的索引
 */
function siftUp(heap, node, i) {
  let index = i;
  while (index > 0) {
    const parentIndex = index - 1 >>> 1;
    const parent = heap[parentIndex];
    if (compare(parent, node) > 0) {
      heap[parentIndex] = node;
      heap[index] = parent;
      index = parentIndex;
    } else {
      return;
    }
  }
}

/**
 * 将堆中的元素向下移动以维护堆的性质
 * @param {Object[]} heap - 堆
 * @param {Object} node - 要移动的节点
 * @param {number} i - 节点在堆中的索引
 */
function siftDown(heap, node, i) {
  let index = i;
  const length = heap.length;
  const halfLength = length >>> 1;
  while (index < halfLength) {
    const leftIndex = (index + 1) * 2 - 1;
    const left = heap[leftIndex];
    const rightIndex = leftIndex + 1;
    const right = heap[rightIndex];
    if (compare(left, node) < 0) {
      if (rightIndex < length && compare(right, left) < 0) {
        heap[index] = right;
        heap[rightIndex] = node;
        index = rightIndex;
      } else {
        heap[index] = left;
        heap[leftIndex] = node;
        index = leftIndex;
      }
    } else if (rightIndex < length && compare(right, node) < 0) {
      heap[index] = right;
      heap[rightIndex] = node;
      index = rightIndex;
    } else {
      return;
    }
  }
}

/**
 * 比较两个节点
 * @param {Object} a - 第一个节点
 * @param {Object} b - 第二个节点
 * @returns {number} 比较结果,如果 a 小于 b,则返回小于 0 的数,如果 a 等于 b,则返回 0,如果 a 大于 b,则返回大于 0 的数
 */
function compare(a, b) {
  const diff = a.sortIndex - b.sortIndex;
  return diff !== 0 ? diff : a.id - b.id;
}

React18的优先级体系

任务优先级 ===> 事件优先级 ===> 调度优先级