React 任务调度器算法-最小堆

11 阅读2分钟

React 任务调度器使用的数据结构是最小堆,本文主要介绍最小堆的三种操作。

  • peek-获取堆顶元素
  • push-为堆添加元素
  • pop-删除堆顶元素

获取堆顶元素

这个比较简单,不解释了。

export function peek(heap) {
  return heap.length === 0 ? null : heap[0];
}

为堆添加元素

添加元素到最后,从下往上比较,根节点比子节点大就交换,一直比较到堆顶。

// 给堆添加元素
export function push(heap, node) {
  // 1. 把node放到堆的最后
  const index = heap.length;
  heap.push(node);
  // 2. 调整最小堆,从下往上堆化
  siftUp(heap, node, index);
}

// 从下往上堆化
function siftUp(heap, node, i) {
  let index = i;
  while (index > 0) {
    const parentIndex = (index - 1) >>> 1; // 除以 2
    const parent = heap[parentIndex];
    if (compare(parent, node) > 0) {
      // node子节点更小,和根节点交换
      heap[parentIndex] = node;
      heap[index] = parent;
      index = parentIndex;
    } else {
      return;
    }
  }
}

function compare(a, b) {
  const diff = a.sortIndex - b.sortIndex;
  return diff !== 0 ? diff : a.id - b.id;
}

删除堆顶元素

将最后一个元素放置堆顶,从上往下比较根节点和两个子节点,保证根节点最小,一直比较到最后一个非叶子节点。

// 删除堆顶元素
export function pop(heap) {
  if (heap.length === 0) {
    return null;
  }
  const first = heap[0];
  const last = heap.pop()!;
  if (first !== last) {
    // 证明heap中有2个或者更多个元素
    heap[0] = last;
    siftDown(heap, last, 0);
  }

  return first;
}

function siftDown(heap, node, i) {
  let index = i;
  const length = heap.length;
  const halfLength = length >>> 1; // 除以 2
  // 最后一个非叶子节点的索引是 halfLength - 1,因为叶子节点没有子节点,所以不需要进行下沉操作
  while (index < halfLength) {
    const leftIndex = (index + 1) * 2 - 1;
    const left = heap[leftIndex];
    const rightIndex = leftIndex + 1;
    const right = heap[rightIndex]; // right不一定存在,等下还要判断是否存在
    if (compare(left, node) < 0) {
      // left<node
      if (rightIndex < length && compare(right, left) < 0) {
        // right存在,且right<left
        heap[index] = right;
        heap[rightIndex] = node;
        index = rightIndex;
      } else {
        // left更小或者right不存在
        heap[index] = left;
        heap[leftIndex] = node;
        index = leftIndex;
      }
    } else if (rightIndex < length && compare(right, node) < 0) {
      // left>=node && right<node
      heap[index] = right;
      heap[rightIndex] = node;
      index = rightIndex;
    } else {
      // 根节点最小,不需要调整
      return;
    }
  }
}

代码中都有详细的注释,有疑问评论区留言。