76 lines
2.2 KiB
JavaScript
76 lines
2.2 KiB
JavaScript
const numbers = [5, 0, 8, 2, 1, 4, 7, 2, 5, -1]; // 测试用例
|
||
|
||
const vaild = [-1, 0, 4, 2, 1, 8, 7, 5, 5, 2]; // 验证用例
|
||
|
||
/**
|
||
* 自底向上下沉
|
||
* @param {*} nums
|
||
*/
|
||
function buildHeap(nums) {
|
||
const n = nums.length;
|
||
// 寻找最后一个非叶子节点,从这里开始倒序遍历(自底向上)
|
||
for (let i = Math.floor(n / 2 - 1); i >= 0; i--) {
|
||
// 取当前节点左右节点较小的
|
||
let min = i * 2 + 1;
|
||
if (i * 2 + 2 < n && nums[i * 2 + 2] < nums[min]) min = i * 2 + 2;
|
||
let cur = i; // 表示当前节点下沉的位置
|
||
|
||
// 如果父节点大于子节点中较小的哪一个那么就交换位置,如果交换位置之后还是大于当前位置的子节点小的那个,继续下沉
|
||
while (min < n && nums[cur] > nums[min]) {
|
||
[nums[cur], nums[min]] = [nums[min], nums[cur]];
|
||
cur = min;
|
||
min = cur * 2 + 1;
|
||
if (cur * 2 + 2 < n && nums[cur * 2 + 2] < nums[min]) min = cur * 2 + 2;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 原地构建小顶堆(heapify),使用下沉(sift down)法
|
||
* @param {number[]} nums 完全二叉树的层序遍历(原始数组)
|
||
* @returns {number[]} 构建完成的小顶堆(原地修改 nums 并返回)
|
||
*/
|
||
function buildHeapBest(nums) {
|
||
const n = nums.length;
|
||
|
||
// 从最后一个非叶子节点开始,依次向前,对每个节点执行下沉
|
||
for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
|
||
siftDown(nums, i, n);
|
||
}
|
||
|
||
return nums;
|
||
}
|
||
|
||
/**
|
||
* 对 nums[i] 进行下沉调整,确保以 i 为根的子树满足小顶堆性质
|
||
* @param {number[]} nums 堆数组
|
||
* @param {number} i 当前要下沉的节点索引
|
||
* @param {number} n 数组长度(限制范围)yiw
|
||
*/
|
||
function siftDown(nums, i, n) {
|
||
let cur = i;
|
||
|
||
while (true) {
|
||
const left = 2 * cur + 1;
|
||
const right = 2 * cur + 2;
|
||
let smallest = cur;
|
||
|
||
// 与左孩子比较
|
||
if (left < n && nums[left] < nums[smallest]) {
|
||
smallest = left;
|
||
}
|
||
|
||
// 与右孩子比较
|
||
if (right < n && nums[right] < nums[smallest]) {
|
||
smallest = right;
|
||
}
|
||
|
||
// 如果当前节点已经是最小的,堆性质满足,退出循环
|
||
if (smallest === cur) break;
|
||
|
||
// 否则交换并继续下沉
|
||
[nums[cur], nums[smallest]] = [nums[smallest], nums[cur]];
|
||
cur = smallest;
|
||
}
|
||
}
|