Quicksort
Quicksort: partition around a pivot, sort the two sides in place. The partition and its invariant, the recurrence for any split, the worst case n(n-1)/2 on sorted input, the balanced best case, the exact average 2(n+1)H_n - 4n comparisons (about 1.39 n log2 n), why any constant-fraction split is still n log n, randomised pivots, the stack depth and the smaller-part-first rule, and behaviour with equal keys.
Why it exists
Quicksort is the sorting method used most in practice: it sorts in place, needs only a small stack, and its inner loop is very short, so on average it beats merge sort and heap sort by a constant factor. Its weakness is a worst case of on unlucky inputs, and GATE tests exactly this contrast: which inputs are bad, what the recurrence is, and why the average case is still .
Prerequisites
You need divide and conquer and the recurrence (divide and conquer).
Precise definitions
Partition. Choose one key, the pivot, and rearrange a range so that every key to the left of the pivot's final position is the pivot and every key to the right is it (Lomuto) or it (Hoare). After a Lomuto partition (below) the pivot is in its final sorted position; Hoare's original partition instead returns a split point, with every key on the left every key on the right.
Quicksort. Partition the range, then sort the part left of the pivot and the part right of the pivot recursively. There is no merge step: when both recursive calls return the range is sorted.
Lomuto partition (used on this page). The pivot is the last key . A boundary keeps the invariant: positions hold keys pivot, positions hold keys pivot, positions are unexamined. Scan from to ; if pivot, increase and swap with . At the end swap the pivot into position . It makes exactly comparisons.
Hoare partition (the original). Two indices start at the two ends and move towards each other; the left one stops at a key pivot, the right one at a key pivot, the two keys are swapped, and so on until the indices cross. It splits evenly when many keys are equal, because both indices stop on keys equal to the pivot.
Randomised quicksort. Choose the pivot uniformly at random among the keys of the range (for example swap a random key to the end first). The running time then depends on the random choices, not on the input order.
How it works
A real run. The animation sorts with exactly the code shown. In each partition the pivot is the last key; the key under comparison is marked; whenever a key is not larger than the pivot it is swapped to the growing left part; at the end the pivot is swapped into its final place (green).
Quicksort with the last element as pivot (Lomuto partition)
Real runC source
Array a (faded = outside the current call)
State
Blue = the position this step is working on, amber = a position being compared, red = positions just swapped, green = finished or in its final place, a filled blue cell = a value just placed, faded = not part of the structure at this step.
Step 1
Sort 6, 3, 8, 1, 7, 2, 5, 4 with quicksort.
Where the cost is. One partition of keys makes comparisons. If it splits the range into parts of sizes and , then Everything depends on the splits:
- Worst case: always (the pivot is the smallest or the largest key): , exactly comparisons. For the last-element pivot this happens on sorted and on reverse sorted input, and with the Lomuto partition above (which sends keys equal to the pivot to the left) also when all keys are equal.
- Best case: always (the pivot is the median): .
- Any constant-fraction split: if every split is at most , then . The recursion tree has depth between and and every level costs at most , so . A split that is not perfect but is proportional is still fast.
- Average case: for a random order of distinct keys the expected number of comparisons is exactly so , only about more comparisons than the best case.
Why the average is . Call the keys in sorted order. Two keys and are compared at most once, and they are compared exactly when one of them is the first pivot chosen among (if a key strictly between them is chosen first, they are separated and never meet). For a random order each of these keys is equally likely to be first, so the probability is . Summing over all pairs with from to (there are pairs with this ): The same value solves the recurrence .
Measured. The program below counts the comparisons of this very code on sorted, reversed, all-equal and random inputs. The random column is the average of random orders and is close to the exact expectation.
#include <stdio.h>
static long cmps;
static void swap(int *x, int *y) { int t = *x; *x = *y; *y = t; }
static int partition(int a[], int lo, int hi) {
int pivot = a[hi], i = lo - 1;
for (int j = lo; j < hi; j++) {
cmps++;
if (a[j] <= pivot) {
i++;
swap(&a[i], &a[j]);
}
}
swap(&a[i + 1], &a[hi]);
return i + 1;
}
static void quicksort(int a[], int lo, int hi) {
if (lo < hi) {
int p = partition(a, lo, hi);
quicksort(a, lo, p - 1);
quicksort(a, p + 1, hi);
}
}
static long run(int a[], int n) {
cmps = 0;
quicksort(a, 0, n - 1);
for (int i = 1; i < n; i++) if (a[i - 1] > a[i]) return -1;
return cmps;
}
int main(void) {
static int a[1024];
int ns[] = {16, 64, 256, 1024};
unsigned s = 99;
printf("%5s %9s %9s %9s %12s\n", "n", "sorted", "reversed", "all-equal", "random-total");
for (int q = 0; q < 4; q++) {
int n = ns[q];
for (int i = 0; i < n; i++) a[i] = i;
long c1 = run(a, n);
for (int i = 0; i < n; i++) a[i] = n - i;
long c2 = run(a, n);
for (int i = 0; i < n; i++) a[i] = 7;
long c3 = run(a, n);
long total = 0; /* 400 random permutations: Fisher-Yates with a simple generator */
for (int trial = 0; trial < 400; trial++) {
for (int i = 0; i < n; i++) a[i] = i;
for (int i = n - 1; i > 0; i--) { s = s * 1103515245u + 12345u; int j = (int)((s >> 8) % (unsigned)(i + 1)); swap(&a[i], &a[j]); }
total += run(a, n);
}
printf("%5d %9ld %9ld %9ld %12ld\n", n, c1, c2, c3, total);
}
return 0;
} n sorted reversed all-equal random-total
16 120 120 120 20509
64 2016 2016 2016 144000
256 32640 32640 32640 850120
1024 523776 523776 523776 4521100Comparisons of quicksort, measured: start
Computed by a script, not typed| n | sorted | reversed | all equal | random (average of 400) | 2(n+1)H_n - 4n | n log2 n |
|---|---|---|---|---|---|---|
| 16 | ||||||
| 64 | ||||||
| 256 | ||||||
| 1024 |
Step 0
Quicksort with the last element as pivot and the partition of this page, on four kinds of input. The random column is the average over 400 random orders of the keys.
Choosing the pivot. A fixed rule (first, last) is defeated by sorted input, which is common in practice. Remedies: choose a random key (expected comparisons for every input, because the randomness is in the algorithm), or the median of three (first, middle, last keys), or the true median found in linear time by the median-of-medians method (divide and conquer applications), which makes the worst case at the price of a large constant.
Stack space. The recursion depth is the height of the recursion tree: for balanced splits and for the worst case. The space of quicksort is therefore on average but in the worst case, unless the algorithm recurses on the smaller part and handles the larger part in a loop (tail-call elimination): each recursive call then handles at most half of the keys, so the depth is at most , whatever the input.
Equal keys. The Lomuto partition above sends equal keys to the left, so a range of equal keys splits every time and costs (the table shows for all-equal input). Hoare's partition stops on equal keys and splits such a range in the middle. The three-way partition (keys , , the pivot) puts all keys equal to the pivot in place at once, which makes all-equal input take .
Properties. In place (apart from the stack); not stable (the partition swaps keys across the pivot, over equal keys); not adaptive in the sense of insertion sort (sorted input is the worst case for the simple pivot rules); average , worst case . Practical versions switch to insertion sort for ranges below about ten keys.
Worked examples
EX 1One partition
Easywith pivot (the last key). Scan: stays; : , swap : ; ; : , swap : ; ; : , swap : ; . Finally swap the pivot into position : . Seven comparisons; the pivot is in its final place, with keys to its left and to its right.
EX 2Comparisons on sorted input
EasyFor keys already in increasing order, with the last key as pivot, every partition peels off one key: comparisons (the table's rule ), against about on a random order. The recursion depth is .
EX 3A lopsided but proportional split
MediumSuppose the partition always puts of the keys on one side and on the other: . The shallowest path has depth , the deepest , and each level costs at most ; the total is between and , so . The constant is larger than for balanced splits but the order is the same. By contrast a split of one key against the rest is .
EX 4The probability argument for two keys
MediumTake four keys and call them in sorted order. Consider and . They are compared only if the first pivot among is or : probability . If is chosen first, and go to different sides and are never compared. For adjacent keys the probability is : they are always compared (nothing can separate them). For and it is , which is why the extremes are rarely compared.
EX 5Recurrences and space
GATE level(a) Quicksort always picks the median as pivot (found for free): . (b) Always the smallest: . (c) A partition that splits into and : . (d) The recursion depth and stack in the worst case: frames, space; with the smaller part first, at most frames. (e) Which input is worst for quicksort with the last element as pivot? Sorted or reverse-sorted input (and all-equal input for the Lomuto partition above). (f) Is quicksort stable? No: for the partition swaps with and gives .
Cheat box
Cheat box
- Partition: pivot to its final place, left, right; comparisons (Lomuto, last pivot).
- . Worst: , comparisons (sorted, reversed, and all-equal for Lomuto). Best/balanced: .
- Average (random order or random pivot): .
- Any split with both sides a constant fraction of is .
- In place, not stable, not adaptive. Space: average stack, worst; smaller-part-first gives .
- Pivot rules: random, median of three, median of medians (worst case ).
- Equal keys: Lomuto with degrades to ; Hoare and three-way partition are fine.
Traps and GATE patterns
Connections
Builds on
Across subjects
No cross-subject links yet.
Quicksort is an instance of divide and conquer with all the work in the divide step; its recurrence is solved with the methods of solving recurrences. Compare it with merge sort and heap sort in stability and comparison of sorts. Its partition also finds the -th smallest key in expected linear time (quickselect, divide and conquer applications).
PYQs
7 previous year questions from your book. I re-solved each one myself (4 by running or simulating, 3 by reasoning only); the book's key is shown next to my answer. All PYQs
An array of 25 distinct elements is to be sorted using quicksort. Assume that the pivot element is chosen uniformly at random. The probability that the pivot element gets placed in the worst possible location in the first round of partitioning (rounded off to 2 decimal places) is
The worst-case running time of Insertion sort, Merge sort and Quick sort, respectively, are:
- (a)θ(nlogn), θ(nlogn) and θ(n2)
- (b)θ(n2) , θ(n2) and θ(nlogn)
- (c)θ(n2), θ(nlogn) and θ(nlogn)
- (d)θ(n2), θ(nlogn) and θ(n2)
Suppose you are provided with the following
function declaration in the C programming
language.
int partition(int a[ ], int n);
The function treats the first element of a[] as a
pivot, and rearranges the array so that all elements
less than or equal to the pivot is in the left part of
the array, and all elements greater than the pivot
is in the right part. In addition, it moves the pivot
so that the pivot is the last element of the left part.
The return value is the number of elements in the
left part. The following partially given function in
the C programming language is used to find the
k-th smallest element in an array a[ ] of size n
using the partition function. We assume k <= n
C
int kth_smallest(int a[], int n, int k)
{
int left_end = partition(a, n);
if ( left_end+1 == k ){
return a[left_end];
}
if ( left_end+1 > k ){
return kth_smallest(
);
} else {
return kth_smallest(
);
}
}
The missing arguments lists are respectively- (a)(a, left_end, k) and (a + left_end +1, n- left_end -1, k-left_end -1)
- (b)( a, left_end, k ) and ( a, n-left_end -1, k- left_end -1 )
- (c)( a + left_end +1, n-left_end -1, k- left_end -1 ) and ( a, left_end, k )
- (d)(a, n-left_end -1, k-left_end -1) and (a, left_end, k)
Which one of the following is the recurrence equation for the worst case time complexity of the Quicksort algorithm for sorting n ( >=2) numbers? In the recurrence equations given in the options below, c is a constant.
- (a)T(n)=2T(n/2)+cn
- (b)T(n)=T(n-1)+T(1)+cn
- (c)T(n)=2T(n-1)+cn
- (d)T(n)=T(n/2)+cn
You have an array of n elements. Suppose you implement quick sort by always choosing the central element of the array as the pivot. Then the tightest upper bound for the worst case performance is
- (a)O(n2)
- (b)O(nlogn)
- (c)θ(nlogn)
- (d)O(n3)
Let P is a quick sort program to sort numbers in ascending order using the first element as the pivot. Let t1 and t2 be the number of comparisons made by P for the inputs [1 2 3 4 5] and [4 1 5 3 2] respectively. Which one of the following holds?
- (a)t1 = 5
- (b)t1 < t2
- (c)t1 > t2
- (d)t1 = t2
In quick sort, for sorting n elements, the (n/4)th smallest element is selected as pivot using an O(n) time algorithm. What is the worst case time complexity of the quick sort?
- (a)θ(n)
- (b)θ(nlogn)
- (c)θ(n2)
- (d)θ(n2logn)
Self-check
Question 1
How many comparisons does quicksort (last element as pivot) make on an array of equal keys with the Lomuto partition shown here?
Show answerHide
: every key is the pivot, so each partition leaves an empty right part and a left part one key shorter.
Question 2
Why is the average case of quicksort although a single partition can be very unbalanced?
Show answerHide
On average the splits are balanced enough: two keys are compared with probability and summing gives ; also any proportional split gives .
Question 3
How can the stack depth of quicksort be limited to in the worst case?
Show answerHide
Recurse on the smaller part only and handle the larger part with a loop: each recursive call is on at most half the keys.
Question 4
Is quicksort stable?
Show answerHide
No: the partition swaps keys across the pivot, which can move a key past an equal key.
Revision card
One-screen revision
- Partition then recurse; .
- Worst (): sorted, reversed, all-equal (Lomuto). Best and average ; average .
- Proportional splits are ; random pivot gives expected bound on every input.
- In place, unstable; stack average, worst, with smaller-part-first.
- Three-way partition handles duplicates.
References
- ·Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (3rd edition), Chapter 7 Quicksort
- ·Sedgewick and Wayne, Algorithms (4th edition), Section 2.3 Quicksort
- ·Knuth, The Art of Computer Programming, Volume 3, Section 5.2.2 Sorting by exchanging
Verification
Quicksort with the Lomuto partition and the last element as pivot was re-implemented in Python and run on EVERY permutation of up to 9 keys: the average number of comparisons equals 2(n+1)H_n - 4n exactly (Fractions), the worst case equals n(n-1)/2, the partition invariants hold after every partition, the recursion depth on sorted input is n - 1, and the smaller-part-first version never needed a depth above floor(lg n) + 1. The expected-value recurrence C(n) = n - 1 + (2/n) sum C(k) was solved exactly for n up to 39 and the pair-probability argument (two keys are compared with probability 2/(j - i + 1)) reproduces the same sum algebraically. The C program on this page produced the table (random column: 400 random orders, within 4 percent of the exact expectation) and the animation is a real run of the shown code. Hoare partition, median-of-three and the three-way partition are described from the standard texts and were not separately measured. Textbook chapter references are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (loop counts, recurrences, merge and quicksort counts and averages, hash probes and load-factor formulas, DP tables, MST and shortest-path runs, Floyd matrices); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; The definition of partition said the pivot is in its final place for every partition scheme; it now says this holds for the Lomuto partition and that Hoare's original partition returns a split point. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)