React Scheduler 最小堆实现小结
作者:_DoubleL
本文主要介绍了React Scheduler 最小堆实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
1. 什么是最小堆
最小堆(Min Heap)是一种完全二叉树结构,同时满足一个很关键的性质:任意一个节点的值,都 ≤ 它的左右子节点的值,也就是说: 👉 堆顶(根节点)一定是整个结构中最小的元素
完全二叉树的特点是:
- 从上到下
- 从左到右依次填满
- 只允许最后一层不满

2. 用数组表示最小堆🔥
最小堆通常用数组实现,而不是链表。
索引: 0 1 2 3 4 5
数组: [1, 3, 5, 4, 6, 8]
如果数组下标从 0 开始:
- 父节点:
(i - 1) >>> 1; - 左子节点:
2 * i + 1 - 右子节点:
2 * i + 2
3. React Scheduler 最小堆实现
React 的 Scheduler 内部,正是通过 最小堆 + sortIndex 来维护任务执行顺序
export type Node = {
id: number; // 每个任务的唯一标识
sortIndex: number; // 决定任务顺序
};
堆的本质:数组
export type Heap<T extends Node> = Array<T>;
3.1 取出堆顶元素
export const peek = <T extends Node>(heap: Heap<T>): T | null => {
return heap.length === 0 ? null : heap[0];
};
3.2 插入元素
插入流程:
- 新元素放到数组末尾
- 从下往上进行 堆化(siftUp),不断与父节点比较,若比父节点小就交换,一直向上,直到满足堆性质
- 恢复最小堆性质
如下图,在最后的节点插入1, 1和父节点(10)交换位置, 接着 1和父节点(2) 交换位置,就变成了恢复了最小堆

// 插入元素
export const push = <T extends Node>(heap: Heap<T>, node: T): void => {
// 1. 把node放到堆的最后
const index = heap.length;
heap.push(node);
// 2. 调整最小堆,从下往上堆化
siftUp(heap, node, index);
};
// 从下往上堆化
export const siftUp = <T extends Node>(
heap: Heap<T>,
node: T,
i: number,
): void => {
let index = i;
while (index > 0) {
// 无符号右移,相当于 /2 并且向下取整
const parentIndex = (index - 1) >>> 1;
const parent = heap[parentIndex];
// 如果父节点大于node,需要交换
if (compare(parent, node) > 0) {
// node子节点更小,和根节点交换
heap[parentIndex] = node;
heap[index] = parent;
index = parentIndex;
} else {
return;
}
}
};
// 比较函数,返回值大于0 表示 a大于b
function compare(a: Node, b: Node) {
const diff = a.sortIndex - b.sortIndex;
return diff !== 0 ? diff : a.id - b.id;
}
3.3 删除堆顶元素
删除流程
- 保存堆顶元素(最小值)
- 取出最后一个元素
- 放到堆顶
- 从上往下堆化(siftDown),比较
node与左右子节点,选择 更小的那个子节点,若子节点更小,则交换,一路向下,直到恢复堆序
// 删除堆顶元素 先取出堆顶元素,然后取出最后一个元素放到堆顶,然后从上往下堆化
export const pop = <T extends Node>(heap: Heap<T>): T | null => {
if (!heap.length) 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<T extends Node>(heap: Heap<T>, node: T, i: number): void {
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]; // 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;
}
}
}
// 比较函数,返回值大于0 表示 a大于b
function compare(a: Node, b: Node) {
const diff = a.sortIndex - b.sortIndex;
return diff !== 0 ? diff : a.id - b.id;
}
到此这篇关于React Scheduler 最小堆实现小结的文章就介绍到这了,更多相关React Scheduler 最小堆内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
