algorighm/heap/base/topk问题.js

87 lines
2.6 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.

/*
topk 问题是一个经典的问题我们维持一个大小为k的堆如果要求最大的第三个数我们就维持小顶堆反之维持大小为k的大顶堆
为什么这有点反直觉为什么求第k大的数要维持小顶堆而不是大顶堆这是因为第k大的数是数组中k个最大的数中最小的那一个
用小顶堆来维持那么要求的第k个最大的数刚好在小顶堆的堆顶
*/
const numbers = [5, 0, 8, 2, 1, 4, 7, 2, 5];
/**
*
* @param {number[]} nums
* @param {number} k
*/
function topK(nums, k) {
const heap = Array(k).fill(-Infinity); // 定义一个大小为k数组用来维护堆
for (let i = 0; i < nums.length; i++) {
// 如果当前元素大于堆顶元素,替换堆顶元素然后下沉调整堆
if (nums[i] > heap[0]) {
heap[0] = nums[i];
let cur = 0; // cur指向下沉元素的下标,一开始就在堆顶
while (true) {
const left = cur * 2 + 1;
const right = cur * 2 + 2;
let smallest = cur;
if (left < k && heap[left] < heap[smallest]) smallest = left;
if (right < k && heap[right] < heap[smallest]) smallest = right;
// 如果smallest 和cur相等表示当前元素来到了合适的位置cur结束下沉
if (smallest === cur) break;
// 交换位置下沉
[heap[cur], heap[smallest]] = [heap[smallest], heap[cur]];
cur = smallest;
}
}
}
// 返回堆顶元素
return heap[0];
}
/**
* 使用小顶堆找出数组中第 K 大的数
* @param {number[]} nums
* @param {number} k
* @returns {number}
*/
function topKbest(nums, k) {
const heap = nums.slice(0, k); // 先取前 k 个元素构建小顶堆
// 下沉函数(维护堆性质)
function siftDown(heap, start, heapSize) {
let cur = start;
while (true) {
const left = 2 * cur + 1;
const right = 2 * cur + 2;
let smallest = cur;
if (left < heapSize && heap[left] < heap[smallest]) smallest = left;
if (right < heapSize && heap[right] < heap[smallest]) smallest = right;
if (smallest === cur) break;
[heap[cur], heap[smallest]] = [heap[smallest], heap[cur]];
cur = smallest;
}
}
// 构建小顶堆(自底向上建堆)
for (let i = Math.floor(k / 2) - 1; i >= 0; i--) {
siftDown(heap, i, k);
}
// 遍历剩余元素,维护一个大小为 k 的小顶堆
for (let i = k; i < nums.length; i++) {
if (nums[i] > heap[0]) {
heap[0] = nums[i];
siftDown(heap, 0, k);
}
}
// 堆顶即为第 k 大元素
return heap[0];
}