难度:简单
题目:
设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。
请实现 KthLargest 类:
- KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
- int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。
示例:
输入:
[“KthLargest”, “add”, “add”, “add”, “add”, “add”]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]
解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3); // return 4
kthLargest.add(5); // return 5
kthLargest.add(10); // return 5
kthLargest.add(9); // return 8
kthLargest.add(4); // return 8
提示:
1 <= k <= 104
0 <= nums.length <= 104
-104 <= nums[i] <= 104
-104 <= val <= 104
最多调用 add 方法 104 次
题目数据保证,在查找第 k 大元素时,数组中至少有 k 个元素
解题思路
为了设计这样一个类,我们可以使用最小堆(优先队列)来实现。
最小堆是一种特殊的完全二叉树,其中每个父节点的值都小于或等于其子节点的值。
在本问题中,我们维护一个大小为k的最小堆,这样堆顶元素就是当前数据流中的第k大元素。
每次插入新元素时,若堆的大小未达到k,则直接将新元素添加到堆中;若堆的大小已经达到k,且新元素大于堆顶元素,则替换堆顶元素并重新调整堆。这样,堆顶元素始终是当前数据流中的第k大元素。
JavaScript实现:
class MinHeap {
constructor() {
this.heap = [];
}
insert(val) {
this.heap.push(val);
this.bubbleUp(this.heap.length - 1);
}
bubbleUp(index) {
while (index > 0) {
let parent = Math.floor((index - 1) / 2);
if (this.heap[parent] <= this.heap[index]) break;
[this.heap[parent], this.heap[index]] = [this.heap[index], this.heap[parent]];
index = parent;
}
}
extractMin() {
const min = this.heap[0];
this.heap[0] = this.heap.pop();
this.bubbleDown(0);
return min;
}
bubbleDown(index) {
while (true) {
let leftChild = 2 * index + 1;
let rightChild = 2 * index + 2;
let smallest = index;
if (leftChild < this.heap.length && this.heap[leftChild] < this.heap[smallest]) {
smallest = leftChild;
}
if (rightChild < this.heap.length && this.heap[rightChild] < this.heap[smallest]) {
smallest = rightChild;
}
if (smallest === index) break;
[this.heap[smallest], this.heap[index]] = [this.heap[index], this.heap[smallest]];
index = smallest;
}
}
}
class KthLargest {
constructor(k, nums) {
this.k = k;
this.minHeap = new MinHeap();
nums.forEach(num => this.minHeap.insert(num));
while (this.minHeap.heap.length > k) {
this.minHeap.extractMin();
}
}
add(val) {
if (this.minHeap.heap.length < this.k) {
this.minHeap.insert(val);
} else if (val > this.minHeap.heap[0]) {
this.minHeap.extractMin();
this.minHeap.insert(val);
}
return this.minHeap.heap[0];
}
}
这里首先定义了一个MinHeap类,用于实现最小堆的功能,包括插入元素、提取最小元素以及调整堆。然后在KthLargest类中,构造函数接受一个整数k和一个初始的整数数组nums,初始化最小堆并填充nums中的元素,保证堆的大小不超过k。add方法用于向数据流中添加新元素,并返回当前第k大的元素。如果堆未满,则直接插入;如果堆已满且新元素更大,则替换堆顶元素并重新调整堆。