Heaps · Heap Fundamentals

Kth Largest Element in a Stream

Maintaining a min-heap capped at exactly k elements across a live sequence of incoming values, so that after every insertion the heap's root is always the k-th largest value seen so far - the same fixed-size-heap idea as one-shot kth-largest selection, but applied incrementally instead of over a static array.

  • 5 min read
  • 9 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: a heap as a live work queue
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

A leaderboard keeps receiving scores, and after each one you're asked for the k-th best score so far. Re-sorting everything on each arrival costs O(n log n) per score; the history also grows without bound. Only the current top k can ever matter.

This skill keeps a size-k min-heap alive between calls, so each new value costs O(log k) and memory never grows past k. It also uses a heap as a live work queue that repeatedly combines the two smallest items, which is Huffman's algorithm behind the Connect Ropes problem.

We'll go from simple to more technical:

  1. The pattern: a min-heap of the k largest so far, kept as state across calls.
  2. A second flavor: a heap as a work queue that joins the two smallest and pushes the result back.
  3. Where it shows up (leaderboards, latency alerts, DEFLATE's Huffman codes).
  4. What breaks it: rebuilding per call, fewer than k values, sorting once, int totals.

The approach

Java

class KthLargest {
    private final PriorityQueue<Integer> heap = new PriorityQueue<>();   // min-heap of the k largest so far
    private final int k;

    KthLargest(int k, int[] nums) {
        this.k = k;
        for (int x : nums) add(x);                  // the initial values go through the same path
    }

    int add(int val) {
        heap.offer(val);
        if (heap.size() > k) heap.poll();           // drop the weakest of the kept values
        return heap.peek();                         // the k-th largest so far
    }
}

Python

import heapq


class KthLargest:
    def __init__(self, k: int, nums: list[int]):
        self.heap = []                         # min-heap of the k largest so far
        self.k = k
        for x in nums:
            self.add(x)                        # the initial values go through the same path

    def add(self, val: int) -> int:
        heapq.heappush(self.heap, val)
        if len(self.heap) > self.k:
            heapq.heappop(self.heap)           # drop the weakest of the kept values
        return self.heap[0]                    # the k-th largest so far

It's the size-k min-heap from kth-element selection, kept alive between calls. Each add costs O(log k), memory stays O(k), and anything evicted can never matter again: k larger values are already kept. Before k values have arrived, the root is just the smallest value seen - the question guarantees answers are only needed once k values exist.

A second flavor: a heap as a live work queue

Some problems repeatedly take the smallest items, combine them, and put the result back - the heap is the queue of pending work. Joining ropes of lengths x and y costs x + y, and the joined rope can be joined again. Always join the two shortest:

Java

long connectRopes(int[] ropes) {
    PriorityQueue<Long> heap = new PriorityQueue<>();
    for (int r : ropes) heap.offer((long) r);
    long cost = 0;
    while (heap.size() > 1) {
        long joined = heap.poll() + heap.poll();
        cost += joined;
        heap.offer(joined);                        // the new rope competes again
    }
    return cost;
}

Python

import heapq


def connect_ropes(ropes: list[int]) -> int:
    heap = list(ropes)
    heapq.heapify(heap)
    cost = 0
    while len(heap) > 1:
        joined = heapq.heappop(heap) + heapq.heappop(heap)
        cost += joined
        heapq.heappush(heap, joined)           # the new rope competes again
    return cost

A rope's length is paid again in every later join it's part of, so short ropes should be joined first and long ones last - Huffman's algorithm. Sorting once and joining left to right isn't the same: for 1, 2, 3, 4, 5 it costs 34 instead of 33, because the running total stops competing with the remaining ropes. Four ropes of 10^9 already cost 8 * 10^9, past an int (Python's ints never overflow).

Where you'll see it

Leaderboards ("the score you need to reach the top 100") and alerting on the k-th highest latency in a stream keep a bounded heap alive between updates. Huffman's merge-the-smallest is how DEFLATE (zip, gzip, PNG) builds its prefix codes, and how optimal merge patterns combine sorted files.

Which tool for which job?

You needUse
The k-th largest after every add, fixed ka size-k min-heap kept as state
Any rank after every addan order-statistic tree, or a Fenwick tree over values
Repeatedly combine the two smallesta min-heap work queue (Huffman)
The same, with the input already sortedtwo FIFO queues, O(n)
Values that expire from a windowlazy deletion with live counts

Common mistakes

MistakeWhat happensDo this instead
Rebuilding the heap on each callO(n log n) per addkeep the heap as instance state
Reading the root before k values existit's the smallest so far, not the k-th largestfollow the problem's guarantee, or check the size
Sorting the ropes once1, 2, 3, 4, 5 costs 34 instead of 33push each joined rope back into the heap
int totalsfour ropes of 10^9 overflowaccumulate in a long
Offer and poll on every addabout 17 comparisons per add for k = 100compare with the root first

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why can a value evicted from the size-k heap never become the k-th largest later?

Show answer

When it was evicted, k values at least as large were kept, and values only ever get added. It will always have at least k larger values ahead of it.

2. Why must joined ropes go back into the heap instead of sorting once?

Show answer

A joined rope is a new candidate that may be shorter than ropes still waiting. Sorting once and joining left to right keeps adding to one growing rope - 34 instead of 33 for 1, 2, 3, 4, 5.

3. What does add return before k values have been seen?

Show answer

The smallest value seen so far - the heap holds everything, fewer than k values. It only becomes the k-th largest once at least k values have arrived.

Key takeaways

  • Keep the size-k min-heap as state; each add is O(log k) with O(k) memory.
  • An evicted value can never re-enter the top k.
  • A heap is also a live work queue: repeatedly join the two smallest (Huffman) and push the result back.
  • Accumulate costs in a long.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
k-th largest per addsize-k min-heap as state
Any rank per addorder-statistic or Fenwick tree
Join the two smallestmin-heap work queue
Sorted inputtwo FIFO queues
Expiring valueslazy deletion

Snippets worth knowing by heart

Java

int add(int val) {
    if (heap.size() < k) heap.offer(val);
    else if (val > heap.peek()) { heap.poll(); heap.offer(val); }   // most adds stop here
    return heap.peek();
}

Python

def add(self, val: int) -> int:
    if len(self.heap) < self.k:
        heapq.heappush(self.heap, val)
    elif val > self.heap[0]:
        heapq.heapreplace(self.heap, val)
    return self.heap[0]

The traps

  • Rebuilding per call: each add costs O(n log n). Keep the heap as instance state.
  • Fewer than k values: the root is the smallest so far, not the k-th largest. Check the size, or rely on the guarantee.
  • Sorting once for ropes: joined ropes must compete again, so costs come out too high. Push each joined rope back.
  • int totals: four ropes of 10^9 overflow. Accumulate in a long.

Staff-level points, one line each

  • Root check first: 1,011,489 comparisons against 16,994,769 for a million adds with k = 100.
  • Any rank: an order-statistic tree, or a Fenwick tree over values; it matched sorting after each of 3,000 adds.
  • Sorted ropes: two FIFO queues, O(n), matched the heap on 300 cases.
  • Huffman on a 221-character passage: 843 bits against 1,105 fixed-width and 1,768 as bytes; within 1 bit per character of the entropy.

Say it in an interview

  • "The heap is state: O(log k) per add and O(k) memory."
  • "An evicted value can never come back into the top k."
  • "Joining ropes is Huffman: always the two shortest, and the result goes back in."
  • "If the input is sorted, two queues make it linear."

Practice

This skill comes with 9 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Kth Largest Element in a Stream free

Kth Largest Element in a Stream

MediumProCoding problemJava · Python

Numbers arrive one at a time. Start from an initial batch, and after each new number arrives, report the k-th largest of everything seen so far, the initial batch included. By the time a result is needed, at least k numbers will have arrived.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Minimum Cost to Connect Ropes

MediumProCoding problemJava · Python

You have n ropes. Joining two ropes of lengths x and y makes one rope of length x + y and costs x + y. Join them all into one rope at the smallest total cost.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Kth Largest Element in a Stream free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime