Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 7 – Sorting as a tool

Quickselect for the k-th element

Find the k-th smallest value, such as the median, in expected linear time: random pivots, three-way partitions, and when a library call or a sort wins.

  • Intermediate
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 6 of 30 minutes free
Free: The problem: the median household income. With a pass: the other 15 sections and 3 interview answers, about 24 minutes.

What you will learn

  • Find the k-th smallest element in expected O(n) time with quickselect
  • Use random pivots and a three-way partition to survive sorted input and repeats
  • Argue why the expected work is linear, and when it degrades to quadratic
  • Choose between quickselect, a sort, a heap and library calls such as std::nth_element

Before you start

What you’ll learn · Before you start

What you will learn

  • Find the k-th smallest element in expected O(n) time with quickselect
  • Use random pivots and a three-way partition to survive sorted input and repeats
  • Argue why the expected work is linear, and when it degrades to quadratic
  • Choose between quickselect, a sort, a heap and library calls such as std::nth_element

Before you start

On this page

Quickselect finds the k-th smallest value of an unsorted array, such as its median, in expected O(n) time and without sorting it. It partitions the array around a pivot the way quicksort does, which leaves the pivot at the position it would have in sorted order, and then keeps only the side that holds position k: the other side is never looked at again. C. A. R. Hoare published it as FIND, Algorithm 65 of the Communications of the ACM. A random pivot keeps the expected work linear on every input, and a three-way partition keeps repeated values cheap. This lesson’s problem asks for the median income of a large survey.

The problem: the median household income

The problem

A household survey records the monthly income of n households in rupees, income[0..n − 1], in the order the forms were entered. Return the k-th smallest income, counting k from 1, so that k = (n + 1) / 2 gives the median of an odd-sized survey. Limits: 1 ≤ n ≤ 10⁶, 1 ≤ k ≤ n and 0 ≤ income[i] ≤ 10⁹; incomes repeat often, and the answer should take expected O(n) time.

  1. [42000, 18000, 95000, 27000, 61000] with k = 3 gives 42000, the middle of the five.
  2. [30000, 30000, 30000, 12000] with k = 2 gives 30000: repeats count, so the second smallest is a copy of 30000.
  3. [7] with k = 1 gives 7.
Five incomes as entered and in sorted order; k = 3 asks only for the third sorted position, 42,000.As entered42,00018,00095,00027,00061,000In sorted order (never built in full)18,000127,000242,000361,000495,0005k = 3 asks for one position only: 42,000

Selection asks for one position of the sorted order

Text description of the diagram

The top row shows the survey as entered: 42,000, 18,000, 95,000, 27,000 and 61,000; the box with 42,000 is drawn thicker.

The bottom row shows the same incomes in sorted order, numbered 1 to 5: 18,000, 27,000, 42,000, 61,000 and 95,000. Only position 3, 42,000, is highlighted; the other four are greyed out, because the question never needs them in order. The line under the rows says that k = 3 asks for one position only: 42,000.

The brute force: take out the minimum k times

Remove the smallest value, k times, and the last one removed is the answer. Each removal scans what is left, so the median of n values costs about 3n²/8 steps. Sorting the whole survey and reading one position is the usual shortcut, and the run counts what it pays for:

The minimum k times, and a full sort to read one value Python · brute/select_brute.py
# The k-th smallest income by brute force: take out the smallest value k times. Then the cost of sorting instead.
import random


def kth_by_repeated_minimum(values, k):
    """Removes the minimum k times; returns the last one removed and the number of values looked at."""
    rest = list(values)
    looked = 0
    for _ in range(k):
        looked += len(rest)
        smallest = min(rest)
        rest.remove(smallest)
    return smallest, looked


for incomes, k in (([42000, 18000, 95000, 27000, 61000], 3), ([30000, 30000, 30000, 12000], 2), ([7], 1)):
    print(f"{incomes}, k = {k} -> {kth_by_repeated_minimum(incomes, k)[0]}")

rng = random.Random(5)
survey = [rng.randint(5_000, 400_000) for _ in range(2_001)]
_, looked = kth_by_repeated_minimum(survey, 1_001)
print(f"median of 2,001 incomes: {looked:,} values looked at")
n = 1_000_001
print(f"median of {n:,} incomes: {(n + (n - 500_000)) * 500_001 // 2:,} values to look at")


class Counted:
    """An income that counts how often sorted() compares it (sorted() only uses <)."""
    comparisons = 0

    def __init__(self, value):
        self.value = value

    def __lt__(self, other):
        Counted.comparisons += 1
        return self.value < other.value


sorted(Counted(rng.randint(5_000, 400_000)) for _ in range(100_001))
print(f"sorting 100,001 incomes to read one of them: {Counted.comparisons:,} comparisons")

Output

[42000, 18000, 95000, 27000, 61000], k = 3 -> 42000
[30000, 30000, 30000, 12000], k = 2 -> 30000
[7], k = 1 -> 7
median of 2,001 incomes: 1,502,501 values looked at
median of 1,000,001 incomes: 375,001,250,001 values to look at
sorting 100,001 incomes to read one of them: 1,531,912 comparisons

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 select_brute.py

For 2,001 incomes the median took 1,502,501 values looked at, and for a million incomes it would take about 3.75 × 10¹¹, far beyond any time limit. Sorting 100,001 incomes made 1,531,912 comparisons, an O(n log n) bill for a question about one position: the order of the other 100,000 incomes was never asked for.

What to notice

Only one position of the sorted order matters. A partition around any pivot puts the pivot exactly where a full sort would, with every smaller value before it and every larger value after it, so after one pass you know on which side the answer lies and can drop the other side whole.

Quickselect, step by step Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • The partition invariant, and why the loop always ends
  • Why the expected work is linear: a proof in four lines
  • A dry run on the first example
  • The code in Python, JavaScript, Java, C++ and Go
  • Pivots measured: the quadratic case and the average
  • Three slips, caught by a stress test
  • In Python, measure before you hand-write it
  • Guaranteed linear time, and the library calls
  • Edge cases and common bugs
  • Variants, with answers
  • Follow-up questions, answered
  • Exercise and practice set
    • Select the k-th smallest value without sorting
  • Check yourself
  • Interview questions
    • A service keeps the last 10⁷ request latencies in an int array and must report their median every minute within 200 ms. Compare a sort, a heap and quickselect for the job, with their costs, and say what you would ship.
    • Your quickselect passes the sample tests but times out on two hidden tests with n = 10⁵: one input is already sorted, the other repeats a single value. What is wrong with your partition, and what do you change?
    • A delivery app must return the 20 stores nearest to a customer out of 50,000 candidates, ordered by distance. Would you use quickselect, a heap or a full sort? What does each cost, and what changes if k becomes 5,000?
  • Key takeaways

1 diagram · 5 runnable examples (Build and run all three, C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 23 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.