Heaps · Heap Fundamentals

Heap Kth-Element Selection

Maintaining a fixed-size heap (capped at k elements) while scanning a collection once, so the heap's root always holds exactly the kth-best element seen so far.

  • 5 min read
  • 9 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: quickselect
  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

"What's the 10th largest transaction today?" Sorting all n values costs O(n log n) and holds everything in order just to read one position. When k is small, a heap that remembers only the best k is much cheaper - and for a one-off query on an array, quickselect is cheaper still.

This skill covers the two standard tools: a min-heap capped at k, which works on streams and leaves the input alone, and quickselect, which finds one rank in linear expected time by rearranging an array. It also covers picking between them, and the comparator and index mistakes that make either one quietly wrong.

We'll go from simple to more technical:

  1. The pattern: a min-heap of the k largest so far, evicting the smallest when it grows past k.
  2. A second flavor: quickselect, which partitions and keeps only the side holding the answer.
  3. Where top-k shows up (slowest requests, largest orders, ORDER BY ... LIMIT).
  4. What breaks it: the wrong heap, no size cap, the n - k index, fixed pivots.

The approach

Java

int findKthLargest(int[] nums, int k) {
    PriorityQueue<Integer> heap = new PriorityQueue<>();     // MIN-heap of the k largest so far
    for (int x : nums) {
        heap.offer(x);
        if (heap.size() > k) heap.poll();                     // evict the smallest of the kept ones
    }
    return heap.peek();                                       // the smallest of the k largest
}

Python

import heapq


def find_kth_largest(nums: list[int], k: int) -> int:
    heap = []                                  # MIN-heap of the k largest so far (heapq: functions on a list)
    for x in nums:
        heapq.heappush(heap, x)
        if len(heap) > k:
            heapq.heappop(heap)                # evict the smallest of the kept ones
    return heap[0]                             # the smallest of the k largest

# the standard library does this in one call: heapq.nlargest(k, nums)[-1]

The heap holds the k largest values seen so far, with the smallest of them on top - exactly the one to evict when something bigger arrives. A value that gets evicted has k values at least as large already kept, so it can't be among the k largest. It's a min-heap even though we want the largest, because the heap has to expose the weakest member. Cost: O(n log k) time, O(k) space. When k is close to n, flip it: the k-th largest is the (n - k + 1)-th smallest, so keep that many values in a max-heap. (Python's heapq functions are a min-heap: push negated values, or on Python 3.14+ use heapq.heappush_max and heappop_max.)

A second flavor: quickselect

For an array you can rearrange, partition it as quicksort does and recurse only into the side that holds the answer:

Java

int kthLargest(int[] a, int k) {
    int target = a.length - k;                  // the k-th largest sits here in ascending order
    int lo = 0, hi = a.length - 1;
    Random rnd = new Random();
    while (true) {
        int p = partition(a, lo, hi, lo + rnd.nextInt(hi - lo + 1));
        if (p == target) return a[p];
        if (p < target) lo = p + 1; else hi = p - 1;
    }
}

int partition(int[] a, int lo, int hi, int pivotIndex) {   // Lomuto: smaller values to the left
    swap(a, pivotIndex, hi);
    int store = lo;
    for (int i = lo; i < hi; i++) if (a[i] < a[hi]) swap(a, i, store++);
    swap(a, store, hi);
    return store;
}

Python

import random


def kth_largest(a: list[int], k: int) -> int:
    target = len(a) - k                        # the k-th largest sits here in ascending order
    lo, hi = 0, len(a) - 1
    while True:
        p = partition(a, lo, hi, random.randint(lo, hi))
        if p == target:
            return a[p]
        if p < target:
            lo = p + 1
        else:
            hi = p - 1


def partition(a: list[int], lo: int, hi: int, pivot_index: int) -> int:   # Lomuto: smaller values to the left
    a[pivot_index], a[hi] = a[hi], a[pivot_index]
    store = lo
    for i in range(lo, hi):
        if a[i] < a[hi]:
            a[i], a[store] = a[store], a[i]
            store += 1
    a[store], a[hi] = a[hi], a[store]
    return store

Each round discards one side, so the expected work is n + n/2 + n/4 + ... = O(n) with a random pivot. With a fixed first-element pivot, sorted input discards one element per round: 12.5 million comparisons for 5,000 sorted values versus about 10,000 shuffled. Quickselect also reorders the caller's array.

Where you'll see it

"Top 10 slowest requests in the last hour" and "5 largest orders today" over streams too large to sort are size-k heaps. Monitoring percentiles (p99 latency) over a batch is a selection problem too; databases answer ORDER BY ... LIMIT k with a bounded heap ("top-N sort") instead of sorting every row.

Which tool for which job?

You needUse
The k largest of a streama min-heap capped at k
One rank from an array you may reorderquickselect with a random pivot
k close to nflip it: keep the n - k + 1 smallest
Every rank, or the data sorted anywaysort once
Rank queries as values come and goan order-statistic tree
Many duplicate valuesquickselect with a three-way partition

Common mistakes

MistakeWhat happensDo this instead
A max-heap for the k largestits top is the best value, not the one to evicta min-heap of the k largest
No size cappush everything, pop k: O(n log n)evict as soon as the heap passes k
Index n - k + 1 or kthe wrong rankthe k-th largest is index n - k, ascending
A fixed first pivotO(n^2) on sorted inputa random pivot
(a, b) -> b - a as a max-heapwrong order when values are near the int limitsCollections.reverseOrder()

Check yourself

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

1. Why does finding the k largest use a MIN-heap?

Show answer

The heap must expose the weakest of the kept values, because that's the one to evict when a bigger value arrives. In a min-heap of the k largest, that's the root - and at the end the root is the k-th largest.

2. What does quickselect do, and why is it expected O(n)?

Show answer

It partitions around a pivot and continues only in the side that contains the target index (n - k for the k-th largest). With a random pivot the side shrinks by a constant fraction on average, so the work is n + n/2 + ... = O(n).

3. When is a size-k heap better than quickselect?

Show answer

When the data is a stream or too large to hold in memory, when the input must not be reordered, or when k is small - O(n log k) with O(k) space.

Key takeaways

  • k largest: min-heap capped at k; the root is the k-th largest. O(n log k).
  • For k near n, keep the n - k + 1 smallest in a max-heap.
  • Quickselect: O(n) expected with a random pivot, O(n^2) with a fixed one on sorted input; it mutates the array.
  • The k-th largest is index n - k in ascending order.

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 largest of a streammin-heap capped at k
One rank, array can changequickselect, random pivot
k near nflip to n - k + 1 smallest
Many duplicatesthree-way partition
Guaranteed O(n)median of medians
Ranks with updatesorder-statistic tree

Snippets worth knowing by heart

Java

PriorityQueue<Integer> heap = new PriorityQueue<>();      // k largest: a MIN-heap
for (int x : nums) {
    if (heap.size() < k) heap.offer(x);
    else if (x > heap.peek()) { heap.poll(); heap.offer(x); }
}
// max-heap: new PriorityQueue<>(Collections.reverseOrder()), never (a, b) -> b - a

Python

import heapq
h = nums[:k]
heapq.heapify(h)
for x in nums[k:]:
    if x > h[0]:
        heapq.heapreplace(h, x)                            # pop the root and push x in one step
kth_largest = h[0]

The traps

  • A max-heap for the k largest: its top is the best value, not the one to evict. Keep a min-heap of the k largest.
  • No size cap: pushing everything costs O(n log n). Evict as soon as the heap passes k.
  • The index: n - k + 1 or k picks the wrong rank. The k-th largest is index n - k in ascending order.
  • Fixed pivots and duplicates: sorted or repeated input makes quickselect O(n^2). Pick a random pivot and use a three-way partition.
  • Subtraction comparators and int distances: b - a overflows near the int limits. Use Collections.reverseOrder() and long.

Staff-level points, one line each

  • n = 100,000, k = 10: shuffled took 100,332 comparisons (82 replacements), ascending 439,965, a full sort 1,531,973.
  • Expected replacements on random input: about k ln(n/k).
  • 5,000 equal values: Lomuto quickselect 9,376,249 comparisons, three-way 5,000.
  • Worst-case O(n): median of medians; in practice introselect or Floyd-Rivest.
  • (a, b) -> b - a mis-ordered MIN_VALUE and MAX_VALUE; (50000, 50000) squared in int gave 705032704.

Say it in an interview

  • "A min-heap of size k, because the root is the one to evict."
  • "On random data most values lose to the root, so it's close to one comparison each."
  • "Quickselect is O(n) expected; I'd use a three-way partition if values repeat."
  • "For a guarantee there's median of medians, but random pivots are what you ship."

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 Heap Kth-Element Selection free

Kth Largest Element in an Array

MediumProCoding problemJava · Python

Return the k-th largest value in nums, counting repeats separately: in [5, 5, 3] the second largest is 5.

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

K Closest Points to Origin

MediumProCoding problemJava · Python

Return the k points closest to the origin (0, 0) by Euclidean distance. If points are equally far, prefer the one with the smaller x, then the smaller y. Return them in any order; the driver prints them by distance, then x, then y, one per line.

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

Practice Heap Kth-Element Selection free

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

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