大纲
- 堆尾添加节点功能
- 弹出堆顶节点功能
- 自顶向下排序功能
- 自下向顶排序功能
- 元素大小比较函数
预览
```javascript /**- 例如小顶堆堆:
- 1
- 3 5
- 4 6 8 10
- 数组化: [1, 3, 5, 4, 6, 8, 10];
- */
/**
- 取出堆顶[1]
- ?
- 3 5
- 4 6 8 10
- 数组化: [, 3, 5, 4, 6, 8, 10];
- 将堆尾的节点放入堆顶,然后自顶向下排序
- 10
- 3 5
- 4 6 8
- 数组化: [10, 3, 5, 4, 6, 8];
*/
<a name="IK8Bw"></a>
### compare
```javascript
function compare(a, b) {
return a - b;
}
siftDown(自顶向下排序)
```javascript /** - 将堆尾的节点放入堆顶,然后自顶向下排序
- 10
- 3 5
- 4 6 8
- 数组化: [10, 3, 5, 4, 6, 8]; *
- 数组长度为6, 只要下个遍历的父节点 大于或者等于 数组的一半,就代表没有子节点,可以结束遍历
- 左子节点: (当前父节点 + 1) * 2 - 1;
- 假如父节点下标是0,左节点的位置就是 1, 3, 7, 15, 31, 63…
- 可以发现的规律, 父节点的左子节点,一定是 父节点下标 * 2 + 1
leftIdnex = (x << 1) + 1
父节点的右子节点就是左节点 + 1 *
- 当每次比较完父节点跟子节点的大小时
- 假如 下标0的元素是(10), 左子节点下标1的元素是(3),右子节点下标2的元素是(5)
- 比较三个数,下标1的元素(3)最小,将它放入下标0的父节点,将下标0的父节点放入下标1
- 3
- 10 5
- 4 6 8
- 此时 下一个比较的父节点应该是下标1的元素10
- 因为它的节点发生了变化,需要重新比较它的左右子节点 *
- 比较完堆的最后一个父节点的时候,这个堆就已经最小化完成了 */
/**
- @summary堆顶开始比较每个节点的左右子节点与父节点的大小
- @param heap 堆
- @param node 节点元素
- @param i 当前父节点
- @return {void}
*/
function siftDown(
i, ) {heap,
node,
while(parentIndex < halfLength) {let parentIndex = i;
let length = heap.length;
let halfLength = length >>> 1;
} ```const leftIndex = (parentIndex << 1) + 1;
const left = heap[leftIndex];
const rightIndex = leftIndex + 1;
const right = heap[rightIndex];
// 进入到这一步,一定是有左子节点的,因为parentIndex < 堆一半的长度才能进循环
if (compare(left, node) < 0) {
if (rightIndex < length && compare(right, left) < 0) {
heap[parentIndex] = right;
heap[rightIndex] = node;
parentIndex = rightIndex;
} else {
heap[parentIndex] = left;
heap[leftIndex] = node;
parentIndex = leftIndex;
}
} else if (rightIndex < length && compare(right, node) < 0) {
heap[parentIndex] = right;
heap[rightIndex] = node;
parentIndex = rightIndex;
} else {
return;
}
}
siftUp(自下向顶排序)
```javascript /** - 向堆尾添加一个节点,然后自下向顶排序
- 2
- 3 10
- 4 6 8 1
- 数组化: [2, 3, 10, 4, 6, 8, 1]; *
- 先查找到当前添加的节点下标的父节点
- 下标6的父节点下标是2,下标5的父节点下标是2,下标4的父节点下标是1,….
- 6 -> 2, 5 -> 2, 4 -> 1, 3 -> 1, 2-> 0, 1 -> 0
- 有上面的规律可得当前下标的父节点计算方式为 ((子节点 - 1) / 2)向下取整
- parentIndex = (x - 1) >>> 1; *
- 当每次比较完父节点跟子节点的大小时
- 假如 当前堆尾添加了下标为6的元素(1),父节点下标2的元素(10)
- 2
- 3 10
- 4 6 8 1
- 比较两个元素,下标6比下标2的元素小,交换两个下标的元素,并且将下个比较的下标置为这个下标2
- 2
- 3 1
- 4 6 8 10
- 下标为2的元素(2),父节点下标0的元素(1),进行比较,得到
- 1
- 3 2
- 4 6 8 10
- 此时下标为0,表示整个堆都已经最小化完成,并且实例图也表现一致 */
/**
- @summary堆顶开始比较每个节点的左右子节点与父节点的大小
- @param heap 堆
- @param node 节点元素
- @param i 当前添加的节点下标
- @return {void}
*/
function siftUp(
i, ) {heap,
node,
while(index > 0) {let index = i;
}const parentIndex = (index - 1) >>> 1;
const parent = heap[parentIndex];
if (compare(parent, node) > 0) {
heap[parentIndex] = node;
heap[index] = parent;
index = parentIndex;
} else {
return;
}
}<a name="kg5k9"></a>
### push(堆尾添加节点)
```javascript
function push(heap, node) {
// 当前的堆长度,就是节点的下标
const index = heap.length;
// 推入堆尾
heap.push(node);
// 自下向顶排序
siftUp(heap, node, index);
}
pop(堆顶弹出节点)
function pop(heap) {
if (heap.length === 0) {
return null;
}
// 拿出堆顶的节点
const first = heap[0];
// 弹出堆尾的节点
const last = heap.pop();
// 如果两个元素不相等,这个堆至少两个元素
if (first !== last) {
// 将堆尾元素放入堆顶
heap[0] = last;
// 自顶向下比较,最小堆化
siftDown(heap, last, 0);
}
return first;
}