skip to content

questions

4

After one partition pass around a pivot value, what is guaranteed about the array?

level: juniorimportance: must knowfreq 72%

answer

  1. think about what the pass actually checks
  2. each element is compared once, against one value
  3. two regions and a single boundary index
  4. nothing is claimed about order inside a region
  5. region sizes are not promised either

basics

~20 s

A partition pass guarantees only a two-region split: everything left of the boundary is at most the pivot value, everything right is at least it. Neither region is sorted, and the two sides need not be the same size.

solid answer

~40 s

One pass rearranges the elements so that a single boundary index separates two regions: one holds elements that compare at most equal to the pivot, the other elements that compare at least equal to it. That is the whole promise. Inside each region the elements sit in whatever order the swaps left them, the two regions can be wildly different sizes — if every element falls on one side the boundary lands at the very end — and where elements *equal* to the pivot go depends on the scheme. The pass costs `O(n)` time with one comparison per element and `O(1)` extra space, because it works by exchanging elements in place rather than building new collections. Partitioning is a regrouping, not a sort.

go deeper

for a junior

Be ready to state the post-condition in one sentence: one boundary index, elements on one side at most the pivot, on the other at least the pivot, in linear time and constant extra space.

for a middle

Explain why the guarantee is deliberately weak — no ordering inside a region, no balance — and why that weakness is what keeps the pass linear and allocation-free.

for a senior

Show you know which promises a caller may lean on. Whether the pivot lands at its final index is scheme-specific, and code that assumes it across schemes drops elements.

for a principal

Own the framing that partitioning is a predicate regrouping, not a sort. Teams that reach for a full sort when they only need a boundary pay n log n for an O(n) question.

## What a partition pass actually does Partitioning takes a collection and a test — either "compare against this pivot value" or a plain yes/no predicate — and rearranges the elements in place so that all the elements failing the test come before all the elements passing it. It returns (or leaves behind) a **boundary index**: the position where one region ends and the other begins. A useful running example: an incident queue where each record carries a numeric priority, and you want every record above a paging threshold moved to the front so the on-call tooling can walk a contiguous block. One linear sweep with exchanges does it, without allocating a second queue. ## The invariant, stated precisely Write the collection as `a[0..n-1]` and let `b` be the boundary the pass returns. When the pass finishes: - every element in `a[0..b-1]` compares at most equal to the pivot; - every element in `a[b..n-1]` compares at least equal to the pivot. That is the entire post-condition. Everything a beginner tends to *also* believe is absent from it. ## What is guaranteed versus what is not | Claim | True after one pass? | |---|---| | Two regions, separated by a known boundary | Yes | | Each region is internally sorted | No | | The two regions have equal size | No | | The boundary sits near the middle | No | | Relative order of equal elements is kept | No | | The pivot element itself sits at its final sorted index | Only in some schemes | The last row is worth dwelling on. In the common left-to-right scheme the pivot is swapped into the boundary slot at the very end, so it genuinely is in the position it would occupy in the fully sorted collection — one element is permanently placed by one linear pass. In the two-ended scheme the pass returns a split point and never fixes the pivot anywhere in particular; a caller that assumes otherwise and excludes the returned index from further work will silently drop an element. So "partition places the pivot" is a property of a *specific* scheme, not of partitioning. ## Why the split can be maximally uneven If the pivot happens to be the largest value present, every other element compares at most equal to it and the boundary lands at the last index, leaving the other region empty. Nothing is broken — the invariant still holds, the pass still cost `O(n)` — but a caller that assumed "about half" has assumed something the pass never promised. Balance is a property of *which pivot you picked*, not of the partitioning machinery. ## Cost One pass examines each element once, so the time is linear in the number of elements, `O(n)`, with `n - 1` or `n` comparisons depending on how the pivot is excluded. Three-way variants may compare an element twice, which is still `O(n)`. Extra space is `O(1)` because the algorithm only ever swaps elements already present — no second collection, no copy. That is exactly why the technique earns its place: it is the cheapest possible way to convert "which group does this belong to?" into "which side of index `b` does it sit on?". ## Pivot versus predicate The pivot framing ("at most this value" / "at least this value") is the one that shows up inside sorting, but the same two-pointer machinery works for any predicate: contractor versus employee, expired versus live, above threshold versus below. With a predicate there is no pivot element to place, so only the boundary is meaningful. Recognising that the pivot is just a convenient way to *express* a predicate is what lets you reuse the pattern outside sorting. ## The interview failure mode Asked "what does partition give you?", weak candidates answer "it sorts the halves" or "it puts the pivot in the middle". Both overclaim. The correct answer is deliberately modest — one boundary, two unordered regions, linear time, constant extra space — and the modesty is the point: a guarantee that weak is cheap enough to run inside a loop, and it is still strong enough that a caller who only needs to know *which side* an element is on never has to look further.

  • If every element compares at most equal to the pivot, where does the boundary end up?
    At the far end, leaving the other region empty. The invariant still holds and the pass still costs `O(n)` — an empty side is a legal outcome, not a bug. It is a reminder that balance depends on the pivot value, and that any caller consuming the two regions must handle one of them being empty.
  • Does a partition pass need a pivot element taken from the collection at all?
    No. The pivot is just a convenient way to express a predicate. Partition by "expired or live", "contractor or employee", "above threshold or below" and the same swap machinery works. The only thing you lose is the notion of placing the pivot itself, since there is no distinguished element — you get a boundary and nothing more.
  • How many comparisons does a single pass make?
    About one per element, so `n` or `n - 1` depending on whether the pivot slot is excluded from the sweep. Three-way schemes that separate "less", "equal" and "greater" may test an element twice, which is still linear. Comparison count, not swap count, is what makes the pass `O(n)`.

It is sorting mail into two bins by postcode prefix: afterwards the bins are cleanly separated, but the letters inside each bin are still in whatever order they arrived.

saying these in an interview costs you the question

  • Says the collection is sorted once partitioning finishes
  • Assumes the pivot always lands near the middle
  • Expects the two regions to come out the same size
  • Claims each region is internally ordered
  • Assumes every scheme leaves the pivot at its final index

context

open as a page

In a three-way Dutch national flag partition, why must mid not advance after a swap with the high region?

level: middleimportance: must knowfreq 60%

basics

~20 s

Because the record swapped in from the high end has never been examined. A low-side swap brings back an element already scanned and classified, so mid may advance; skipping the newly arrived one would file it in the wrong region.

open as a page

Why does Lomuto's partition perform more swaps than Hoare's on duplicate-heavy input?

level: middleimportance: should knowfreq 44%

basics

~20 s

Lomuto swaps for every element that compares at most equal to the pivot — with many duplicates, nearly all of them. Hoare swaps only when both pointers have stopped on misplaced elements, fixing two positions per exchange.

open as a page

After an in-place partition splits payroll rows into contractors and employees, is the original row order preserved?

level: seniorimportance: should knowfreq 36%

basics

~20 s

No. In-place partition schemes are unstable: they exchange rows across long distances, so rows within each group come out reordered. Preserving order costs O(n) extra space in one pass, or O(n log n) time in place.

open as a page