2Brute2Best
🔥0

Kth Largest Element in a Stream

Easy·LeetCode #703Basic Anchor
LeetCode
30-Second Mental ModelHeap — Top K (Streaming / dynamic: min-heap of size k)
Complexity:O(N log N)→O(log k)
Key Insight

Keep a min-heap that holds only the k largest numbers seen so far. On each add, push the value, then pop the smallest if the heap has more than k items. Its top is the smallest of the k largest, which is the kth largest overall.

Core Validity Invariant

The heap always holds exactly the k largest numbers seen so far. That makes its top, the smallest of them, the kth largest in the whole stream.

01

Problem

Build a class KthLargest that keeps a stream of numbers and can tell you its kth largest value at any moment.

  • KthLargest(int k, vector<int>& nums) starts the stream with the numbers in nums.
  • int add(int val) adds val to the stream and returns the kth largest number in the stream now.

"kth largest" means: sort the numbers from biggest to smallest and count down from the top. If a number appears twice, each copy counts as its own number.

Input

The integer k, the starting list nums, and then a series of add(val) calls.

Output

For each add call, the kth largest number after that value is added.

Example 1
Input: k = 3, nums = [4, 5, 8, 2]; then add(3), add(5), add(10), add(9), add(4)
Output: 4, 5, 5, 8, 8

The stream starts as 8, 5, 4, 2, so the 3rd largest is 4. After add(3) the list is 8, 5, 4, 3, 2 and the 3rd is still 4. After add(5) it is 8, 5, 5, 4, 3, 2, so the 3rd is 5. add(10) gives 10, 8, 5, 5, 4, 3, 2, and the 3rd is 5 again. add(9) pushes it to 8, and add(4) leaves it at 8.

Example 2
Input: k = 1, nums = []; then add(-3), add(-2), add(-7)
Output: -3, -2, -2

With k = 1 the answer is simply the largest number so far. After -3 it is -3. After -2 the largest is -2. Adding -7 does not change it.

Constraints
  • • 0 ≤ nums.length ≤ 10^4
  • • 1 ≤ k ≤ nums.length + 1
  • • −10^4 ≤ nums[i] ≤ 10^4
  • • −10^4 ≤ val ≤ 10^4
  • • At most 10^4 calls will be made to add
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    The answer is a rank, not a value. If the stream holds 8, 5, 5, 4, which number is the 3rd largest?

  2. 2.

    If I kept every number and sorted on each add, how much work would one add cost?

  3. 3.

    Suppose k numbers in the stream are already bigger than some value x. Can x ever be the kth largest after that?

  4. 4.

    Which heap keeps the smallest item on top, and could that smallest item be the one I throw away?

  5. 5.

    The question only asks about the kth largest. Do I really need to remember every number to answer it forever?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceKeep every number, sort the whole list on each add, read position kO(N log N)O(N)
OptimalKeep only the k largest in a min-heap; the top is the kth largestO(log k)O(k)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “kth largest”A rank counted from the top. The answer is the smallest of the k largest, so a size-k group is enough.
  • “a stream, with add(val)”Numbers keep arriving. The answer must be ready after every add, without re-reading old numbers.
  • “returns the kth largest after each add”Keep a size-k min-heap: one push and at most one pop per add.
Stream of values+answer = kth largest+keep only the best k→Heap — Top K (Streaming / dynamic: min-heap of size k)

The Kth Largest Element in an Array question asks for one answer over a fixed array. This question must answer after every add, so the heap has to stay correct as numbers arrive. The rule that makes it work is throw away the smallest as soon as there are k + 1 numbers. For the k smallest numbers, flip the heap to a max-heap; the same idea applies.

10

Don't make this mistake

Pattern confusion: keeping every number in the heap
✗ heap.push(val); return heap.top(); // min-heap of ALL numbers
✓ heap.push(val); if ((int)heap.size() > k) heap.pop(); return heap.top();

A min-heap of everything has the overall smallest on top, not the kth largest. On the dry run, after the constructor, the heap holds {2, 4, 5, 8}, so the top is 2. The 3rd largest of 8, 5, 4, 2 is 4, but this version returns 2. After add(3), the heap is {2, 3, 4, 5, 8} and it still returns 2. The heap must be cut back to k.

Using the default max-heap
✗ priority_queue<int> heap; // max-heap
✓ priority_queue<int, vector<int>, greater<int>> heap; // min-heap

A max-heap's top is the largest number. Popping when the size is above k then removes the biggest number and keeps the k smallest. On the dry run, the constructor leaves {2, 4, 5} in the max-heap, so the first answer would be 5 instead of 4.

Popping before pushing
✗ if ((int)heap.size() == k) heap.pop(); heap.push(val);
✓ heap.push(val); if ((int)heap.size() > k) heap.pop();

Popping first removes the smallest of the k numbers before we know whether val is bigger. On add(3) with the heap {4, 5, 8}, the pop-first version removes 4, pushes 3, and returns 3. The correct answer is 4, because 3 is the smallest of the four candidates and must be the one dropped.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Heap — Top K translate into production architectures and low-level memory mechanics.