algorighm/heap/base/原地构建.js

76 lines
2.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}