The quicksort algorithm is a divide and conquer algorithm. Quicksort first divides a large array into two smaller sub-arrays: the low elements and the high elements. Quicksort can then recursively sort the sub-arrays. The steps are:
The base case of the recursion is an array of size zero or one, which is in order by definition and requires no further sorting.
The pivot selection and partitioning steps can be done in several different ways; the choice of specific implementation schemes greatly affects the algorithm's performance.
procedure quick_sort(array : list of sortable items, start : first element of list, end : last element of list) if start < end pivot_point ← partition(array, start, end) quick_sort(array, start, pivot_point - 1) quick_sort(array, pivot_point + 1, end) end if end procedure
procedure partition(array : list of sortable items, start : first element of list, end : last element of list) mid ← (start + end) / 2 swap array[start] and array[mid] pivot_index ← start pivot_value ← array[start] scan ← start + 1 while scan <= end if array[scan] < pivot_value pivot_index ← pivot_index + 1 swap array[pivot_index] and array[scan] end if scan ← scan + 1 end while swap array[start] and array[pivot_index] return pivot_index end procedure
In this example, the quicksort partition()
function is called with the arguments array
(the array to partition), 0 (the subscript of the first element of the array), and 11 (the subscript of the last element of the array).
Begin partion
mid ← (start + end) / 2 // (0 + 11) / 2 = 5
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
31 | 81 | 22 | 48 | 13 | 67 | 93 | 14 | 45 | 58 | 79 | 72 |
start
|
mid
|
end
|
Swap first and middle elements
swap array[start] and array[mid]
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 81 | 22 | 48 | 13 | 31 | 93 | 14 | 45 | 58 | 79 | 72 |
start
|
mid
|
end
|
Compute pivot_index
, pivot_value
, and scan
pivot_index ← start pivot_value ← array[start] scan ← start + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 81 | 22 | 48 | 13 | 31 | 93 | 14 | 45 | 58 | 79 | 72 |
start p_ndx
|
scan
|
mid
|
end
|
scan <= end, loop entered. array[scan] (81) not < pivot_value (67).
scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 81 | 22 | 48 | 13 | 31 | 93 | 14 | 45 | 58 | 79 | 72 |
start p_ndx
|
scan
|
mid
|
end
|
scan <= end, loop continues. array[scan] (22) < pivot_value (67).
pivot_index ← pivot_index + 1 swap array[pivot_index] and array[scan] scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 22 | 81 | 48 | 13 | 31 | 93 | 14 | 45 | 58 | 79 | 72 |
start |
p_ndx
|
scan
|
mid
|
end
|
scan <= end, loop continues. array[scan] (48) < pivot_value (67).
pivot_index ← pivot_index + 1 swap array[pivot_index] and array[scan] scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 22 | 48 | 81 | 13 | 31 | 93 | 14 | 45 | 58 | 79 | 72 |
start |
p_ndx
|
scan
|
mid
|
end
|
scan <= end, loop continues. array[scan] (13) < pivot_value (67).
pivot_index ← pivot_index + 1 swap array[pivot_index] and array[scan] scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 22 | 48 | 13 | 81 | 31 | 93 | 14 | 45 | 58 | 79 | 72 |
start |
p_ndx
|
mid scan
|
end
|
scan <= end, loop continues. array[scan] (31) < pivot_value (67).
pivot_index ← pivot_index + 1 swap array[pivot_index] and array[scan] scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 22 | 48 | 13 | 31 | 81 | 93 | 14 | 45 | 58 | 79 | 72 |
start |
p_ndx
|
mid |
scan
|
end
|
scan <= end, loop continues. array[scan] (93) not < pivot_value (67).
scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 22 | 48 | 13 | 31 | 81 | 93 | 14 | 45 | 58 | 79 | 72 |
start |
p_ndx
|
mid |
scan
|
end
|
scan <= end, loop continues. array[scan] (14) < pivot_value (67).
pivot_index ← pivot_index + 1 swap array[pivot_index] and array[scan] scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 22 | 48 | 13 | 31 | 14 | 93 | 81 | 45 | 58 | 79 | 72 |
start |
mid p_ndx
|
scan
|
end
|
scan <= end, loop continues. array[scan] (45) < pivot_value (67).
pivot_index ← pivot_index + 1 swap array[pivot_index] and array[scan] scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 22 | 48 | 13 | 31 | 14 | 45 | 81 | 93 | 58 | 79 | 72 |
start |
mid |
p_ndx
|
scan
|
end
|
scan <= end, loop continues. array[scan] (58) < pivot_value (67).
pivot_index ← pivot_index + 1 swap array[pivot_index] and array[scan] scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 22 | 48 | 13 | 31 | 14 | 45 | 58 | 93 | 81 | 79 | 72 |
start |
mid |
p_ndx
|
scan
|
end
|
scan <= end, loop continues. array[scan] (79) not < pivot_value (67).
scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
67 | 22 | 48 | 13 | 31 | 14 | 45 | 58 | 93 | 81 | 79 | 72 |
start |
mid |
p_ndx
|
end
scan
|
scan <= end, loop continues. array[scan] (72) not < pivot_value (67).
scan ← scan + 1
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | ||
array
|
67 | 22 | 48 | 13 | 31 | 14 | 45 | 58 | 93 | 81 | 79 | 72 | |
start |
mid |
p_ndx
|
end
|
scan
|
scan > end, loop ends.
swap array[start] and array[pivot_index]
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | ||
array
|
58 | 22 | 48 | 13 | 31 | 14 | 45 | 67 | 93 | 81 | 79 | 72 | |
start |
mid |
p_ndx
|
end
|
scan
|
Array is now partitioned.
return pivot_index
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | [11] | |
array
|
58 | 22 | 48 | 13 | 31 | 14 | 45 | 67 | 93 | 81 | 79 | 72 |
start |
mid |
p_ndx
|
end
|
Time Complexity: O(n log n)
Space Complexity: O(log n)
The worst case time complexity for quicksort is O(n2) and the worst case space complexity is O(n). If the pivot happens to be the smallest or largest element in the list (or in some implementations, if all of the elements are equal) one of the sublists produced by the partitioning process may end up as size n - 1.
If that happens repeatedly in every partition, then each recursive call processes a list of size one less than the previous list. That will require n - 1 nested calls before we reach the base case of a list with size 1 and the recursive call tree will be a linear chain of n - 1 nested calls.