Introduction

Quicksort sorts by divide and conquer, but puts the work in the divide step. It chooses a pivot, partitions the array so that smaller elements come before larger ones, and then sorts the two parts recursively; no combine step is needed. It sorts in place, and its average running time is Θ(nlg⁡n)\Theta(n \lg n) with small constants, which is why it is often the fastest sort in practice. Its worst case, however, is Θ(n2)\Theta(n^2), and the gap between the two cases is the centre of the analysis below.

Prerequisites

The algorithm

Pseudocode uses 1-based arrays, <- for assignment and A[i..j] for the subarray from position ii to position jj.

Text
QUICKSORT(A, p, r)
    if p < r
        q <- PARTITION(A, p, r)
        QUICKSORT(A, p, q - 1)
        QUICKSORT(A, q + 1, r)

Initial call: QUICKSORT(A, 1, n). After PARTITION, the pivot is at its final sorted position qq, every element of A[p..q−1]A[p..q-1] is ≤A[q]\le A[q], and every element of A[q+1..r]A[q+1..r] is >A[q]> A[q] (for the Lomuto scheme below).

Lomuto partition (CLRS 3e)

The pivot is the last element x=A[r]x = A[r].

Text
PARTITION(A, p, r)
    x <- A[r]
    i <- p - 1
    for j <- p to r - 1
        if A[j] <= x
            i <- i + 1
            exchange A[i] with A[j]
    exchange A[i + 1] with A[r]
    return i + 1

Invariant at the start of each iteration: A[p..i]≤xA[p..i] \le x, A[i+1..j−1]>xA[i+1..j-1] > x, A[j..r−1]A[j..r-1] not yet examined, A[r]=xA[r] = x.

Trace on A=[2,8,7,1,3,5,6,4]A = [2, 8, 7, 1, 3, 5, 6, 4], pivot x=4x = 4:

jjA[j]A[j]A[j]≤4A[j] \le 4?ii afterArray after the step
12yes, exchange A[1]A[1] with itself12 8 7 1 3 5 6 4
28no12 8 7 1 3 5 6 4
37no12 8 7 1 3 5 6 4
41yes, exchange A[2]A[2], A[4]A[4]22 1 7 8 3 5 6 4
53yes, exchange A[3]A[3], A[5]A[5]32 1 3 8 7 5 6 4
65no32 1 3 8 7 5 6 4
76no32 1 3 8 7 5 6 4
finalexchange A[4]A[4], A[8]A[8]2 1 3 4 7 5 6 8

PARTITION returns q=4q = 4. It makes exactly r−p=7r - p = 7 key comparisons, n−1n - 1 for a subarray of nn elements, on every input. It executes the exchange line 4 times (at j=1j = 1, 4 and 5, plus the final one); if exchanges of an element with itself are not counted, that is 3. Questions that ask for "the number of swaps" usually intend one of these two conventions, so check which.

Hoare partition

The pivot is the first element x=A[p]x = A[p]; two indices move towards each other and exchange out-of-place pairs.

Text
HOARE-PARTITION(A, p, r)
    x <- A[p]
    i <- p - 1
    j <- r + 1
    while TRUE
        repeat j <- j - 1 until A[j] <= x
        repeat i <- i + 1 until A[i] >= x
        if i < j then exchange A[i] with A[j]
        else return j

With Hoare partition the recursive calls are QUICKSORT(A, p, j) and QUICKSORT(A, j + 1, r). It guarantees every element of A[p..j]A[p..j] is ≤\le every element of A[j+1..r]A[j+1..r], but the pivot is not necessarily at its final position, and jj is not the pivot's index.

Trace on A=[5,3,8,4,9,1,6,2,7]A = [5, 3, 8, 4, 9, 1, 6, 2, 7], x=5x = 5: first jj stops at 8 (A[8]=2A[8] = 2) and ii at 1 (A[1]=5A[1] = 5); exchange to get 2 3 8 4 9 1 6 5 7. Next jj stops at 6 (value 1) and ii at 3 (value 8); exchange to get 2 3 1 4 9 8 6 5 7. Next jj stops at 4 (value 4) and ii at 5 (value 9); now i>ji > j, so return j=4j = 4. The parts are [2,3,1,4][2, 3, 1, 4] and [9,8,6,5,7][9, 8, 6, 5, 7]; the pivot 5 sits inside the right part.

Hoare's scheme makes about three times fewer exchanges than Lomuto's on average and handles many equal keys far better, because both indices stop on keys equal to the pivot.

Lomuto and Hoare partition traces
Figure 1. Left: Lomuto partition of 2 8 7 1 3 5 6 4 around 4, showing the regions ≤x\le x, >x> x and unexamined after each step. Right: Hoare partition of 5 3 8 4 9 1 6 2 7 around 5, showing the two exchanges and the crossing point j=4j = 4.

Analysis

Let T(n)T(n) be the time on nn elements. Partition costs Θ(n)\Theta(n). If it produces parts of sizes kk and n−1−kn - 1 - k:

T(n)=T(k)+T(n−1−k)+Θ(n).T(n) = T(k) + T(n - 1 - k) + \Theta(n).

Worst case: Θ(n2)\Theta(n^2)

The worst split is 00 and n−1n - 1 at every level: T(n)=T(n−1)+Θ(n)=Θ(n2)T(n) = T(n-1) + \Theta(n) = \Theta(n^2). With the last-element pivot this happens on already sorted input, on reverse-sorted input, and (for Lomuto) on input where all keys are equal. The number of comparisons is then (n−1)+(n−2)+⋯+1=n(n−1)/2(n-1) + (n-2) + \dots + 1 = n(n-1)/2, and the recursion depth is n−1n - 1.

Best case: Θ(nlg⁡n)\Theta(n \lg n)

For distinct keys, if every pivot is the median, T(n)=2T(n/2)+Θ(n)=Θ(nlg⁡n)T(n) = 2T(n/2) + \Theta(n) = \Theta(n \lg n).

Any constant-proportion split: Θ(nlg⁡n)\Theta(n \lg n)

Even a persistently lopsided split such as 1 to 9 gives T(n)=T(n/10)+T(9n/10)+Θ(n)T(n) = T(n/10) + T(9n/10) + \Theta(n). The recursion tree has full levels of cost at most cncn and depth log⁡10/9n=Θ(lg⁡n)\log_{10/9} n = \Theta(\lg n), so T(n)=Θ(nlg⁡n)T(n) = \Theta(n \lg n). Only splits that are lopsided by a non-constant factor, such as a constant number of elements on one side, cause quadratic time.

Recursion trees for balanced and worst-case quicksort
Figure 2. Left: the recursion tree for a 1-to-9 split, with full levels costing cncn and depth log⁡10/9n\log_{10/9} n. Right: the worst-case tree, a path of subproblem sizes n,n−1,n−2,…,1n, n-1, n-2, \ldots, 1 with level costs summing to Θ(n2)\Theta(n^2).

Average case: Θ(nlg⁡n)\Theta(n \lg n)

For the shown Lomuto partition, assume distinct keys throughout this randomized analysis. Assume all n!n! orderings of distinct keys are equally likely, or use randomized quicksort, which picks the pivot uniformly at random from A[p..r]A[p..r] (exchange it with A[r]A[r], then run PARTITION). Let z1<z2<⋯<znz_1 < z_2 < \dots < z_n be the keys in sorted order. Two keys are compared at most once, only when one of them is the first pivot chosen from {zi,…,zj}\{z_i, \ldots, z_j\}, which happens with probability 2/(j−i+1)2/(j - i + 1). Summing over all pairs,

E[comparisons]=∑i<j2j−i+1=2(n+1)Hn−4n=2nln⁡n+O(n)≈1.39 nlg⁡n,E[\text{comparisons}] = \sum_{i<j} \frac{2}{j - i + 1} = 2(n+1)H_n - 4n = 2n \ln n + O(n) \approx 1.39\, n \lg n,

where HnH_n is the nn-th harmonic number. For randomized quicksort this expectation holds for every input of distinct keys: no particular input is bad, only unlucky random choices are, and they are very unlikely.

Small cases make the formula concrete. For n=3n = 3 it gives 8⋅116−12=838 \cdot \tfrac{11}{6} - 12 = \tfrac{8}{3}, and for n=4n = 4 it gives 10⋅2512−16=29610 \cdot \tfrac{25}{12} - 16 = \tfrac{29}{6}; averaging Lomuto quicksort's comparison count over all 6 (or 24) input orders gives exactly these values. The pair probability is useful on its own: adjacent keys zi,zi+1z_i, z_{i+1} are always compared (probability 2/2=12/2 = 1), since no key between them can separate them, while the smallest and largest keys are compared with probability only 2/n2/n (one of them must be the very first pivot).

Space

Quicksort uses O(1)O(1) auxiliary space besides the recursion stack, so it is called in place. The stack depth is Θ(lg⁡n)\Theta(\lg n) on balanced splits but Θ(n)\Theta(n) in the worst case. Recursing on the smaller part first and looping on the larger (tail-call elimination) bounds the depth by O(lg⁡n)O(\lg n) in every case.

Summary of cases

CaseWhen (last-element pivot)ComparisonsTimeStack depth
Bestdistinct keys; pivot is always the medianabout nlg⁡nn \lg nΘ(nlg⁡n)\Theta(n \lg n)Θ(lg⁡n)\Theta(\lg n)
Average or randomizeddistinct keys: random order, or random pivot2nln⁡n+O(n)2n \ln n + O(n)Θ(nlg⁡n)\Theta(n \lg n)Θ(lg⁡n)\Theta(\lg n) expected
Worstsorted, reverse sorted, or all equal (Lomuto)n(n−1)/2n(n-1)/2Θ(n2)\Theta(n^2)Θ(n)\Theta(n)

Properties and practical improvements

  • Not stable. Long-distance exchanges reorder equal keys. Example with Lomuto: 2a,2b,12_a, 2_b, 1 partitions around pivot 1 to give 1,2b,2a1, 2_b, 2_a.
  • In place (apart from the stack) and not adaptive: sorted input is the worst case for the naive pivot choice.
  • Pivot choice. Random pivot, or median-of-three (median of first, middle and last), avoids the sorted-input worst case. With distinct keys for this partition, choosing the exact median with the linear-time selection algorithm guarantees Θ(nlg⁡n)\Theta(n \lg n) in the worst case but is slow in practice.
  • Many equal keys. A three-way partition (Dutch national flag) splits into <x< x, =x= x and >x> x and does not recurse on the middle part, so an all-equal array takes Θ(n)\Theta(n).
  • Small subarrays. Switching to insertion sort below a small cut-off saves time.
  • Hybrids. Introsort runs quicksort but switches to heapsort if the recursion becomes too deep, keeping the worst case at O(nlg⁡n)O(n \lg n).

Quickselect: the $k$-th smallest element

Related material. Selection is not a separate item of the GATE syllabus. It is included because it reuses PARTITION and is commonly studied together with quicksort.

The same partition finds the kk-th smallest element without sorting: partition, then recurse into one side only.

Text
QUICKSELECT(A, p, r, k)          // distinct keys; k-th smallest of A[p..r], 1 <= k <= r - p + 1
    if p = r then return A[p]
    q <- RANDOMIZED-PARTITION(A, p, r)
    m <- q - p + 1                 // rank of the pivot within A[p..r]
    if k = m then return A[q]
    else if k < m then return QUICKSELECT(A, p, q - 1, k)
    else return QUICKSELECT(A, q + 1, r, k - m)

For distinct keys, the displayed Lomuto-based Quickselect has expected time Θ(n)\Theta(n) (the expected sizes shrink geometrically) and worst case Θ(n2)\Theta(n^2). The median-of-medians pivot rule (groups of 5) gives worst-case Θ(n)\Theta(n) under the same distinct-key condition. To retain these linear guarantees with duplicate keys, use a three-way partition and return immediately when k lies in the equal block (CLRS 3e, Section 9.3).

Quickselect time and workspace. Here n=r−p+1≥1n=r-p+1\ge1, comparisons, swaps and random-index generation are constant-time, and each pivot is chosen uniformly in the current subarray. Best time is Θ(n)\Theta(n), when the first partition finds the target. Without tail-call elimination, the displayed one-subproblem recursion has Θ(1)\Theta(1) best call-stack space, O(lg⁡(n+1))O(\lg(n+1)) expected space and Θ(n)\Theta(n) worst space, with constant-size frames and no copied subarrays; input storage and the scalar result are excluded. Replacing the single recursive call by a loop gives O(1)O(1) auxiliary words with the same time bounds. These randomized stack bounds do not analyze the median-of-medians pivot subroutine; a straightforward implementation that copies medians into separate arrays uses O(n)O(n) auxiliary words including its stack.

Edge cases

  • n=0n = 0 or 11: p < r is false; nothing happens.
  • Two elements: one partition with one comparison.
  • All keys equal: Lomuto puts everything on the left (every A[j]≤xA[j] \le x), producing the worst case; Hoare splits near the middle.
  • Lomuto partitions on the last element. Questions that say "first element as pivot" mean a variant: exchange A[p]A[p] with A[r]A[r] first, or use Hoare's scheme; check which.

Important properties and observations

  • After one Lomuto partition, the pivot is in its final position; after a Hoare partition, that is not guaranteed.
  • The number of comparisons in one Lomuto partition of nn elements is always n−1n - 1.
  • Worst-case recurrence T(n)=T(n−1)+nT(n) = T(n-1) + n; best T(n)=2T(n/2)+nT(n) = 2T(n/2) + n; 1-to-(c−1)(c-1) split for constant cc still gives Θ(nlg⁡n)\Theta(n \lg n).
  • Randomization changes the guarantee from "average over inputs" to "expected for every input of distinct keys (for the shown partition)".

GATE examination perspective

  • Partition traces. the array after the first partition, the pivot's final index, or the number of exchanges. State the scheme (Lomuto or Hoare) and the pivot position before tracing.
  • Worst-case recognition. sorted or reverse-sorted input with first or last pivot; all-equal keys with Lomuto.
  • Probability questions. ziz_i and zjz_j are compared with probability 2/(j−i+1)2/(j - i + 1): 1 for neighbours in sorted order, 2/n2/n for the minimum and maximum.
  • Recurrences for given splits. "the pivot always splits off n/5n/5 elements" gives T(n)=T(n/5)+T(4n/5)+Θ(n)=Θ(nlg⁡n)T(n) = T(n/5) + T(4n/5) + \Theta(n) = \Theta(n \lg n); "the pivot always leaves 5 elements on one side" gives T(n)=T(n−6)+T(5)+Θ(n)=Θ(n2)T(n) = T(n - 6) + T(5) + \Theta(n) = \Theta(n^2).
  • Properties. in place, not stable, average Θ(nlg⁡n)\Theta(n \lg n), worst Θ(n2)\Theta(n^2), O(lg⁡n)O(\lg n) stack when the smaller side is recursive and the larger side is iterative.

Summary

Sources

  1. T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Introduction to Algorithms, 3rd ed., MIT Press, 2009, Chapter 7 (Sections 7.1 to 7.4: PARTITION, performance, randomized quicksort and its expected-comparison analysis; Problem 7-1 on Hoare partition; Problem 7-4 on stack depth) and Chapter 9 (Sections 9.2 and 9.3: randomized and worst-case linear-time selection).

Next steps