Kth Largest Element in a Stream (LC 703)
On this page
Pattern: Top K Elements
Difficulty: Easy
Key Concept: Keep a min-heap of size k so the smallest value in the heap is always the k-th largest in the entire stream seen so far.
Problem Statement
Design a class KthLargest that finds the k-th largest element in a stream of integers.
- Constructor
KthLargest(int k, int[] nums)initializes the object with the integerkand the initial elements of the streamnums. - Method
int add(int val)appendsvalto the stream and returns the element representing the k-th largest value in the stream after includingval.
Input / Output
k: positive integer (you always have at leastkelements in the stream after eachadd, per problem constraints).nums: initial array (may have length< kin some variants; the official problem guarantees enough elements after adds).add(val): returns the k-th largest among all values seen so far (initial + all previousaddcalls).
Example (conceptual)
k = 3,nums = [4, 5, 8, 2]→ after setup, 3rd largest is4.- After
add(3), stream contains4,5,8,2,3→ 3rd largest is still4.
1. Algorithm & Pseudocode
Optimal idea
- Use a min-heap (smallest element at the top) with at most
kelements. - After processing any set of values, the
klargest values in the stream sit inside the heap. - The smallest among those
kis exactly the k-th largest overall →peek().
Pseudocode (optimal)
class KthLargest(k, nums):
heap = empty min-heap
for x in nums:
heap.push(x)
if heap.size > k:
heap.popMin() // remove the smallest of the (k+1) values
function add(val):
heap.push(val)
if heap.size > k:
heap.popMin()
return heap.peek()
Brute force idea
- Keep all numbers in a list (initial
numsplus everyadd). - On each
add, sort the list descending (or ascending and index from the end). - Return the element at position
k - 1in the “largest first” order.
2. Step-by-Step Analysis (Beginner-Friendly)
- Why a heap at all? Sorting the whole stream every time is simple but costs
O(m log m)peraddifmis the current stream length. A heap capped atkkeeps only what you need. - Why a min-heap for “k-th largest”? You want the
klargest values. If you keep more thank, drop the smallest among them—that is the min-heappoll(). What remains are theklargest. - Why is
peek()the answer? Among theklargest numbers, the smallest one is ranked exactly k-th when you sort descending (there arek - 1numbers larger than it inside the heap, and everything outside the heap is ≤ it). - Constructor vs
add: Same rule: push, then if size >k, remove the minimum. That keeps the invariant after initial values and after every new value. - Brute force “why it’s slow”: Sorting repeatedly does redundant work; you only need order information for the top
k, not a full sort of all elements every time.
3. The Dry Run
Parameters: k = 3, nums = [4, 5, 8, 2], then add(3), add(5), add(10), add(9), add(4).
We trace the optimal min-heap (size ≤ 3). Internal heap order is not fully sorted; we show contents as a multiset and peek() (minimum in heap = k-th largest in stream).
Phase A — Constructor (nums processed in order)
| Step | Action | Heap contents (multiset) | Heap size | peek() = 3rd largest |
|---|---|---|---|---|
| init | — | [] |
0 | — |
| 1 | push 4 |
{4} |
1 | — |
| 2 | push 5 |
{4,5} |
2 | — |
| 3 | push 8 |
{4,5,8} |
3 | 4 |
| 4 | push 2 → size 4 → poll min |
{4,5,8} |
3 | 4 |
Check: Stream {4,5,8,2} sorted desc: 8,5,4,2 → 3rd largest = 4.
Phase B — add operations
| Call | Stream (all values) | After push | Size > 3? | After poll if needed |
peek() (return) |
|---|---|---|---|---|---|
add(3) |
…,3 |
{3,4,5,8} |
yes → remove min 3 |
{4,5,8} |
4 |
add(5) |
…,5 |
{4,5,5,8} |
yes → remove min 4 |
{5,5,8} |
5 |
add(10) |
…,10 |
{5,5,8,10} |
yes → remove min 5 |
{5,8,10} |
5 |
add(9) |
…,9 |
{5,8,9,10} |
yes → remove min 5 |
{8,9,10} |
8 |
add(4) |
…,4 |
{4,8,9,10} |
yes → remove min 4 |
{8,9,10} |
8 |
Verification (full sort descending after each add):
- After
add(3):8,5,4,3,2→ 3rd =4. - After
add(5):8,5,5,4,3,2→ 3rd =5. - After
add(10):10,8,5,5,4,3,2→ 3rd =5. - After
add(9):10,9,8,5,5,4,3,2→ 3rd =8. - After
add(4):10,9,8,5,5,4,4,3,2→ 3rd =8.
4. Java Solution
Brute Force
import java.util.*;
class KthLargestBrute {
private final int k;
private final List<Integer> data = new ArrayList<>();
public KthLargestBrute(int k, int[] nums) {
this.k = k;
for (int x : nums) {
data.add(x);
}
}
public int add(int val) {
data.add(val);
Collections.sort(data, Collections.reverseOrder());
return data.get(k - 1);
}
}
- Time: Each
addsortsO(m log m)wheremis current stream size. - Space:
O(m)for the list.
Optimal
import java.util.PriorityQueue;
class KthLargest {
private final int k;
private final PriorityQueue<Integer> minHeap = new PriorityQueue<>();
public KthLargest(int k, int[] nums) {
this.k = k;
for (int x : nums) {
minHeap.offer(x);
if (minHeap.size() > k) {
minHeap.poll();
}
}
}
public int add(int val) {
minHeap.offer(val);
if (minHeap.size() > k) {
minHeap.poll();
}
return minHeap.peek();
}
}
- Time:
O(n log k)for constructor (n = nums.length), eachaddisO(log k). - Space:
O(k)for the heap.
5. The “Java vs. Others” Edge
PriorityQueueis a min-heap by default — ideal for “k largest” (evict the global minimum among your candidates). In C++,std::priority_queueis a max-heap by default, so you either invert comparisons or use a custom comparator / store negatives.- Python
heapqis also a min-heap; the same “size-k + pop smallest” pattern applies. - API clarity:
offer/poll/peekexpress intent for interview code;add/removealso exist onQueuebutpollavoids exceptions on empty (here size is guarded). - Class design: The object stores
kand the heap;addmutates shared state — matches how LeetCode expects an object with persistent stream state.
6. Complexity Summary
| Approach | Time | Space | Notes |
|---|---|---|---|
| Brute Force | O(m log m) per add |
O(m) |
m = stream length; simple but does not scale. |
| Optimal | O(log k) per add; O(n log k) init |
O(k) |
Min-heap of size k; peek is k-th largest. |