Quicksort variant that chooses the pivot as the median of the first, middle, and last elements.
Quicksort median of three improves pivot selection by choosing the median value among the first, middle, and last elements of the current range. This avoids some common bad cases, such as already sorted or reverse sorted arrays, where choosing the first or last element as pivot can produce highly unbalanced partitions.
The algorithm keeps the same recursive structure as quicksort. Only the pivot selection step changes.
Problem
Given an array of length , reorder it such that:
Idea
For a range , inspect three candidates:
where
Choose the median of these three values as the pivot.
This is a small sampling strategy. It often gives a better pivot than blindly choosing one endpoint.
Algorithm
median_of_three_quicksort(A, lo, hi):
if lo >= hi:
return
pivot_index = median_of_three(A, lo, hi)
swap A[pivot_index] and A[hi]
p = partition(A, lo, hi)
median_of_three_quicksort(A, lo, p - 1)
median_of_three_quicksort(A, p + 1, hi)median_of_three(A, lo, hi):
mid = (lo + hi) // 2
if A[lo] > A[mid]:
swap A[lo], A[mid]
if A[mid] > A[hi]:
swap A[mid], A[hi]
if A[lo] > A[mid]:
swap A[lo], A[mid]
return midExample
Let:
For the whole range:
| position | value |
|---|---|
| first | 9 |
| middle | 7 |
| last | 1 |
Sorted candidates:
Median pivot:
Partition around :
Then recursively sort the two sides.
Correctness
Median of three changes only the pivot selection rule. Partitioning still places the chosen pivot in its final sorted position, with no larger elements on the left and no smaller elements on the right. The recursive calls sort both sides, so the whole range becomes sorted.
Complexity
| case | time |
|---|---|
| best | |
| average | |
| worst |
Median of three improves the likelihood of balanced partitions, but it does not remove the quadratic worst case.
Space:
| case | space |
|---|---|
| average recursion | |
| worst recursion |
Properties
| property | value |
|---|---|
| stable | no |
| in-place | yes |
| randomized | no |
| pivot quality | better than endpoint pivot |
| implementation cost | low |
Notes
Median of three is a practical default for quicksort implementations. It is especially useful against already sorted input, reverse sorted input, and simple patterned data.
For stronger protection, combine it with introsort, three way partitioning, or randomized fallback.
Implementation
def median_of_three_quicksort(a):
def sort(lo, hi):
if lo >= hi:
return
pivot_index = median_of_three(lo, hi)
a[pivot_index], a[hi] = a[hi], a[pivot_index]
p = partition(lo, hi)
sort(lo, p - 1)
sort(p + 1, hi)
def median_of_three(lo, hi):
mid = (lo + hi) // 2
if a[lo] > a[mid]:
a[lo], a[mid] = a[mid], a[lo]
if a[mid] > a[hi]:
a[mid], a[hi] = a[hi], a[mid]
if a[lo] > a[mid]:
a[lo], a[mid] = a[mid], a[lo]
return mid
def partition(lo, hi):
pivot = a[hi]
i = lo
for j in range(lo, hi):
if a[j] <= pivot:
a[i], a[j] = a[j], a[i]
i += 1
a[i], a[hi] = a[hi], a[i]
return i
sort(0, len(a) - 1)
return afunc MedianOfThreeQuickSort(a []int) {
var sort func(int, int)
sort = func(lo, hi int) {
if lo >= hi {
return
}
pivotIndex := medianOfThree(a, lo, hi)
a[pivotIndex], a[hi] = a[hi], a[pivotIndex]
p := partitionMedianThree(a, lo, hi)
sort(lo, p-1)
sort(p+1, hi)
}
sort(0, len(a)-1)
}
func medianOfThree(a []int, lo, hi int) int {
mid := (lo + hi) / 2
if a[lo] > a[mid] {
a[lo], a[mid] = a[mid], a[lo]
}
if a[mid] > a[hi] {
a[mid], a[hi] = a[hi], a[mid]
}
if a[lo] > a[mid] {
a[lo], a[mid] = a[mid], a[lo]
}
return mid
}
func partitionMedianThree(a []int, lo, hi int) int {
pivot := a[hi]
i := lo
for j := lo; j < hi; j++ {
if a[j] <= pivot {
a[i], a[j] = a[j], a[i]
i++
}
}
a[i], a[hi] = a[hi], a[i]
return i
}