Quicksort

Quicksort is an efficient, in-place $$O(n \log n)$$ sort that uses $$O(\log n)$$ stack memory. Its worst case is $$O(n^2)$$. It is not stable, and not adaptive. A divide-and-conquer algorithm, this uses a pivot in order to split the array into two subarrays, which are lower and higher of the selected pivot respectively.

The partition
A partition is a function that takes an array, chooses an element called the "pivot", and arranges the array in such a way that every element smaller than the pivot is to the front of it and every element larger than the pivot is to the back of it.

The sort
To Quicksort an array, first partition it, then Quicksort left and right of the pivot recursively.

Example
Take the array.

First, partition it (arbitrarily choosing the lowest element as the pivot): [6 3 1 2 5 4] [7] [12 11 15 14 16 8 10 9 13]

Recursively sort the left half:

[3 1 2 5 4] [6] [7] [12 11 15 14 16 8 10 9 13] [1 2] [3] [5 4] [6] [7] [12 11 15 14 16 8 10 9 13] [1] [2] [3] [4] [5] [6] [7] [12 11 15 14 16 8 10 9 13]

Append:

[1 2 3 4 5 6] [7] [12 11 15 14 16 8 10 9 13]

Recursively sort the right half:

[1 2 3 4 5 6] [7] [11 8 10 9] [12] [16 14 15 13] [1 2 3 4 5 6] [7] [8 10 9] [11] [12] [16 14 15 13] [1 2 3 4 5 6] [7] [8] [10] [9] [11] [12] [16 14 15 13] [1 2 3 4 5 6] [7] [8] [10] [9] [11] [12] [13] [14] [15] [16]

Append:

[1 2 3 4 5 6 7 8 10 9 11 12 13 14 15 16]

Sorted!

Pseudocode
There are two main partition functions, Lomuto and Hoare. Lomuto's partition (also known as LL pointers) is easier to understand but slower. Hoare's partition (also known as LR pointers) is slightly more difficult to understand, but it is more efficient because it makes fewer swaps on average than Lomuto's partition. The code for both is displayed below.

Lomuto
Uses the leftmost element as the pivot.

procedure partition(array arr,int lo,int hi) do elem pivot = arr[lo] int i = lo   for j = lo to hi - 1 do if arr[j] <= pivot do swap arr[i] and arr[j] i ++ end if end for return i + 1 end procedure quicksort(array arr,int lo,int hi) do if hi <= lo do return end if int j = partition(arr,lo,hi) quicksort(arr,lo,j - 1) quicksort(arr,j + 1,hi) end

Hoare
Uses the middle element as the pivot.

procedure partition(array arr,int lo,int hi) do elem pivot = arr[floor((lo + hi) / 2)] int i = lo   int j = hi    while true do while arr[i] < pivot i ++ end while while arr[j] > pivot j -- end while if i < j do swap arr[i] and arr[j] i ++; j --; end if else return j      end else end while end procedure quicksort(array arr,int lo,int hi) do if hi <= lo do return end if int j = partition(arr,lo,hi) quicksort(arr,lo,j) quicksort(arr,j,hi) end

Pivot
Sometimes, it is not enough that the first or last element is chosen as the pivot: this causes issues, particularly on already sorted, or reversed data.

To fix this issue, a random element or the middle element can be selected instead.

A better solution would be to use median-of-three partition, which compares three elements: the leftmost, the middle, and the rightmost, and selects based on which is the median.

Few distinct elements
In Lomuto's partition, the algorithm fails to recognize equal elements. This is made worse when the algorithm is sorting a subarray of all equal elements.

This can be fixed by adding a third partition for elements equal to the pivot. Note the partitioning procedure needs to return two values in this case.

Insertion sort
For sorting a subarray with few elements (implementation-defined), insertion sort can be used instead.