Quickselect finds the k-th smallest element in an unsorted array. It uses the same partition idea as Quicksort but only recurses into one side, which reduces work.
You use it when you need a single order statistic such as median, top-k, or percentile, without fully sorting the array.
Problem
Given an array of length and an integer with , find a value such that
Algorithm
Choose a pivot, partition the array into elements less than pivot and greater than pivot, then recurse only into the side that contains the k-th element.
quickselect(A, k):
if length(A) == 1:
return A[0]
pivot = choose_pivot(A)
L = [x in A where x < pivot]
E = [x in A where x == pivot]
R = [x in A where x > pivot]
if k < length(L):
return quickselect(L, k)
else if k < length(L) + length(E):
return pivot
else:
return quickselect(R, k - length(L) - length(E))In practice, this is implemented in-place using partition indices.
Example
Let
Find the element with (0-based), which means the 3rd smallest.
Sorted order would be:
So the answer is:
Quickselect avoids full sorting and isolates the region containing the answer.
Correctness
Partition guarantees that all elements in the left part are smaller than pivot, and all in the right part are larger. Therefore, the rank of the pivot is known after partition. The recursion preserves the invariant that the k-th element lies in the chosen subarray.
Complexity
| case | time |
|---|---|
| best | |
| average | |
| worst |
Worst case occurs when pivot selection is consistently poor. Randomization avoids this in practice.
Space complexity:
for in-place version.
When to Use
Use Quickselect when:
- you need median or k-th element
- you do not need full sorting
- input fits in memory
- average case performance is sufficient
For guaranteed linear time, use Median of Medians, but with higher constant cost.
Implementation
def quickselect(a, k):
def partition(left, right, pivot_index):
pivot = a[pivot_index]
a[pivot_index], a[right] = a[right], a[pivot_index]
store = left
for i in range(left, right):
if a[i] < pivot:
a[store], a[i] = a[i], a[store]
store += 1
a[right], a[store] = a[store], a[right]
return store
left, right = 0, len(a) - 1
while True:
if left == right:
return a[left]
pivot_index = (left + right) // 2
pivot_index = partition(left, right, pivot_index)
if k == pivot_index:
return a[k]
elif k < pivot_index:
right = pivot_index - 1
else:
left = pivot_index + 1func Quickselect(a []int, k int) int {
left, right := 0, len(a)-1
for {
if left == right {
return a[left]
}
pivotIndex := (left + right) / 2
pivotIndex = partition(a, left, right, pivotIndex)
if k == pivotIndex {
return a[k]
} else if k < pivotIndex {
right = pivotIndex - 1
} else {
left = pivotIndex + 1
}
}
}
func partition(a []int, left, right, pivotIndex int) int {
pivot := a[pivotIndex]
a[pivotIndex], a[right] = a[right], a[pivotIndex]
store := left
for i := left; i < right; i++ {
if a[i] < pivot {
a[store], a[i] = a[i], a[store]
store++
}
}
a[right], a[store] = a[store], a[right]
return store
}