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