Kth Largest Element in an Array medium
Problem Statement
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
Example 1:
Input: nums = [3,2,1,5,6,4], k = 2
Output: 5
Example 2:
Input: nums = [3,2,3,1,2,4,5,5,6], k = 4
Output: 4
Steps and Explanation
This problem can be efficiently solved using a min-heap data structure. Here's the breakdown:
-
Min-Heap Initialization: We'll use a min-heap to store the
k
largest elements encountered so far. A min-heap ensures that the smallest element is always at the root. -
Iterating through the Array: We iterate through the input array
nums
. -
Heap Maintenance: For each element
num
innums
:- If the size of the min-heap is less than
k
, we addnum
to the heap. This ensures that we collect the firstk
elements. - If the size of the min-heap is equal to
k
andnum
is greater than the smallest element (root) of the heap, we replace the root withnum
and then "heapify" (re-balance) the heap to maintain the min-heap property. This ensures that we always keep track of thek
largest elements seen so far.
- If the size of the min-heap is less than
-
Returning the Result: After iterating through the entire array, the root of the min-heap will be the kth largest element.
Code (TypeScript)
function findKthLargest(nums: number[], k: number): number {
// Use a min-heap to store the k largest elements.
const minHeap = new MinHeap();
// Iterate through the array.
for (const num of nums) {
// Add to heap if less than k elements.
if (minHeap.size() < k) {
minHeap.insert(num);
} else if (num > minHeap.peek()) {
// Replace smallest if larger than current smallest.
minHeap.extract();
minHeap.insert(num);
}
}
// The root of the heap is the kth largest.
return minHeap.peek();
}
// Min-heap implementation using array. You could also use a library like priority-queue.
class MinHeap {
private heap: number[] = [];
size(): number {
return this.heap.length;
}
peek(): number {
if (this.heap.length === 0) {
throw new Error("Heap is empty");
}
return this.heap[0];
}
insert(value: number): void {
this.heap.push(value);
this.heapifyUp(this.heap.length - 1);
}
extract(): number {
if (this.heap.length === 0) {
throw new Error("Heap is empty");
}
const root = this.heap[0];
this.heap[0] = this.heap.pop()!; //swap last element with root and pop off the last
this.heapifyDown(0);
return root;
}
private heapifyUp(index: number): void {
let currentIndex = index;
while (currentIndex > 0) {
const parentIndex = Math.floor((currentIndex - 1) / 2);
if (this.heap[currentIndex] < this.heap[parentIndex]) {
[this.heap[currentIndex], this.heap[parentIndex]] = [this.heap[parentIndex], this.heap[currentIndex]];
currentIndex = parentIndex;
} else {
break;
}
}
}
private heapifyDown(index: number): void {
let currentIndex = index;
while (true) {
let smallestIndex = currentIndex;
const leftChildIndex = 2 * currentIndex + 1;
const rightChildIndex = 2 * currentIndex + 2;
if (leftChildIndex < this.heap.length && this.heap[leftChildIndex] < this.heap[smallestIndex]) {
smallestIndex = leftChildIndex;
}
if (rightChildIndex < this.heap.length && this.heap[rightChildIndex] < this.heap[smallestIndex]) {
smallestIndex = rightChildIndex;
}
if (smallestIndex !== currentIndex) {
[this.heap[currentIndex], this.heap[smallestIndex]] = [this.heap[smallestIndex], this.heap[currentIndex]];
currentIndex = smallestIndex;
} else {
break;
}
}
}
}
Complexity Analysis
-
Time Complexity: O(N log k), where N is the length of the input array. Building the heap initially takes O(k log k) time, and iterating through the remaining elements takes O((N-k) log k). In the worst case, where k is close to N, this simplifies to O(N log N). If k is significantly smaller than N, this approaches O(N log k) which is much better.
-
Space Complexity: O(k) to store the min-heap. The space used by the heap is proportional to
k
.