Kth Largest Element in a Stream
Kth Largest Element in a Stream: Design and Implementation
The Kth Largest Element in a Stream problem requires designing a system to dynamically track the k-th largest integer in a stream of numbers.
This task demands an efficient approach to handle frequent updates while maintaining the k-th largest element in near real-time.
Problem:
Design a class to find the kth largest integer in a stream of values, including duplicates. E.g. the 2nd largest from [1, 2, 3, 3] is 3. The stream is not necessarily sorted.
Implement the following methods:
- constructor(int k, int[] nums) Initializes the object given an integer k and the stream of integers nums.
- int add(int val) Adds the integer val to the stream and returns the kth largest integer in the stream.
Problem Breakdown
Task
Initialization:
- Create a class KthLargest initialized with:
- An integer k representing the position of the k-th largest element.
- An initial array nums that represents the stream of integers.
- Create a class KthLargest initialized with:
Dynamic Updates:
- A method add(val) adds a new value to the stream and returns the k-th largest element in the updated stream.
Explanation:
KthLargest kthLargest = new KthLargest(3, [1, 2, 3, 3]);
kthLargest.add(3); // return 3
kthLargest.add(5); // return 3
kthLargest.add(6); // return 3
kthLargest.add(7); // return 5
kthLargest.add(8); // return 6
Constraints:
- 1 <= k <= 1000
- 0 <= nums.length <= 1000
- -1000 <= nums[i] <= 1000
- -1000 <= val <= 1000
- There will always be at least k integers in the stream when you search for the kth integer.
Approach
Key Idea: Min-Heap
To efficiently track the k-th largest element, we use a min-heap of size k.
Why Min-Heap?
- A min-heap keeps the smallest element at the root.
- By maintaining a heap of size
k, the root always represents the k-th largest element, as it ensures the heap contains the largestkelements of the stream.
Algorithm
Initialization
- Create a min-heap with the first
kelements ofnums. - If
numshas more thankelements, iterate through the remaining numbers and adjust the heap to maintain only the largestkelements.
Add Method
- Add the new element
valto the heap. - If the heap exceeds size
k, remove the smallest element (heap root). - Return the root of the heap, which is the k-th largest element.
There are mainly two approach to solve this problem –
- Brute Force
- Min Heap
1. Sorting
- Time complexity: O(m∗nlogn)
- Space complexity: O(1) or O(n)depending on the sorting algorithm.
class KthLargest:
def __init__(self, k: int, nums: List[int]):
self.k = k
self.arr = nums
def add(self, val: int) -> int:
self.arr.append(val)
self.arr.sort()
return self.arr[len(self.arr) - self.k]class KthLargest {
public:
vector arr;
int k;
KthLargest(int k, vector& nums) {
this->arr = nums;
this->k = k;
}
int add(int val) {
arr.push_back(val);
sort(arr.begin(), arr.end());
return arr[arr.size() - k];
}
}; class KthLargest {
List arr;
int K;
public KthLargest(int k, int[] nums) {
K = k;
arr = new ArrayList();
for (int i = 0; i < nums.length; i++) {
arr.add(nums[i]);
}
}
public int add(int val) {
arr.add(val);
Collections.sort(arr);
return arr.get(arr.size() - K);
}
} class KthLargest {
/**
* @param {number} k
* @param {number[]} nums
*/
constructor(k, nums) {
this.arr = nums;
this.k = k;
}
/**
* @param {number} val
* @return {number}
*/
add(val) {
this.arr.push(val);
this.arr.sort((a, b) => a - b);
return this.arr[this.arr.length - this.k];
}
}2. Min -Heap
Time & Space Complexity
- Time complexity: O(m∗logk)
- Space complexity: O(k)
class KthLargest {
private:
priority_queue, greater> minHeap;
int k;
public:
KthLargest(int k, vector& nums) {
this->k = k;
for (int num : nums) {
minHeap.push(num);
if (minHeap.size() > k) {
minHeap.pop();
}
}
}
int add(int val) {
minHeap.push(val);
if (minHeap.size() > k) {
minHeap.pop();
}
return minHeap.top();
}
}; class KthLargest {
private PriorityQueue minHeap;
private int k;
public KthLargest(int k, int[] nums) {
this.k = k;
this.minHeap = new PriorityQueue<>();
for (int num : nums) {
minHeap.offer(num);
if (minHeap.size() > k) {
minHeap.poll();
}
}
}
public int add(int val) {
minHeap.offer(val);
if (minHeap.size() > k) {
minHeap.poll();
}
return minHeap.peek();
}
}
class KthLargest:
def __init__(self, k: int, nums: List[int]):
self.minHeap, self.k = nums, k
heapq.heapify(self.minHeap)
while len(self.minHeap) > k:
heapq.heappop(self.minHeap)
def add(self, val: int) -> int:
heapq.heappush(self.minHeap, val)
if len(self.minHeap) > self.k:
heapq.heappop(self.minHeap)
return self.minHeap[0]/**
* const { MinPriorityQueue } = require('@datastructures-js/priority-queue');
*/
class KthLargest {
/**
* @param {number} k
* @param {number[]} nums
*/
constructor(k, nums) {
this.minHeap = new MinPriorityQueue();
this.k = k;
for (const num of nums) {
this.minHeap.enqueue(num);
}
while (this.minHeap.size() > k) {
this.minHeap.dequeue();
}
}
/**
* @param {number} val
* @return {number}
*/
add(val) {
this.minHeap.enqueue(val);
if (this.minHeap.size() > this.k) {
this.minHeap.dequeue();
}
return this.minHeap.front();
}
}Where mm is the number of calls made to add().
