<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. DSA
  3. Searching and sorting
All lessons in this course

Lesson 16 of 50 16 minIntermediate

Quick sort

Partition around a pivot, recurse on both sides, see why a bad pivot makes it O(n²), and how .NET’s introsort and quickselect build on it.

Quick sort is also divide and conquer, but it does the hard work before recursing instead of after. Pick one value, the pivot, and rearrange the array so every smaller value is on its left and every larger value is on its right. The pivot is now in its final position. Then sort the left part and the right part the same way. No merge step is needed.

Interactive

Partitioning around a pivot

Red is the pivot. The amber bar is being checked. Values left of the dashed line are smaller than the pivot. Green bars are in their final place.

InputPivot
Comparisons 0 · Deepest level 0
Quick sort: pick a pivot, move smaller values to its left and bigger ones to its right, then sort each side the same way.

Partitioning (Lomuto scheme)

Put the pivot at the end. Walk through the other values with j, and keep a boundary i: everything before i is smaller than the pivot. When a[j] is smaller, swap it to position i and move the boundary. Finally swap the pivot to position i.

QuickSort.cs
static void QuickSort(int[] a, int lo, int hi){    if (lo >= hi) return;    int p = Partition(a, lo, hi);    // pivot ends at index p, in its final place    QuickSort(a, lo, p - 1);         // smaller values    QuickSort(a, p + 1, hi);         // bigger values} static int Partition(int[] a, int lo, int hi){    int mid = lo + (hi - lo) / 2;    (a[mid], a[hi]) = (a[hi], a[mid]);   // use the middle value as pivot, moved to the end    int pivot = a[hi];     int i = lo;                          // a[lo..i-1] < pivot    for (int 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]);       // pivot into its final place    return i;} int[] data = { 7, 2, 9, 4, 10, 1, 6, 3, 8, 5 };QuickSort(data, 0, data.Length - 1);   // sort the whole arrayConsole.WriteLine(string.Join(" ", data));
Output
1 2 3 4 5 6 7 8 9 10

Good pivots, bad pivots

If the pivot splits the array roughly in half every time, there are about log n levels, each doing O(n) work: O(n log n). If the pivot is always the smallest or largest value, each partition removes only one item. Then there are n levels: O(n²), and the recursion can be deep enough to overflow the stack. Taking the last item as pivot on already sorted data does exactly that; try it in the widget above.

Pivot choiceEffect
First or last itemSimple, but O(n²) on sorted or reversed input, which is common in practice
Middle itemFixes sorted input; some crafted inputs are still bad
Random itemBad cases become extremely unlikely, for any input
Median of three (first, middle, last)Cheap and good in practice; used in many libraries

Quick sort in .NET: introsort

Array.Sort, List<T>.Sort and Span<T>.Sort use introsort (introspective sort): quick sort with a median-of-three pivot, plus two safety nets. Pieces of 16 or fewer items are finished with insertion sort. If the recursion gets suspiciously deep (a bad-pivot input), it switches to heap sort, which is always O(n log n). You get quick sort’s speed with a guaranteed worst case.

PropertyQuick sort
TimeAverage O(n log n), worst O(n²)
Extra memoryO(log n) stack on average (in place otherwise)
StableNo
In practiceUsually the fastest comparison sort on arrays: tight loops, cache-friendly

Bonus: quickselect

To find the k-th smallest value you do not need to sort everything. After one partition you know the pivot’s exact rank. If it is the one you want, stop; otherwise continue in one side only. On average that is n + n/2 + n/4 + … ≈ 2n steps: O(n).

QuickSelect.cs
// k = 1 means the smallest valuestatic int KthSmallest(int[] a, int k){    int lo = 0, hi = a.Length - 1, target = k - 1;    while (true)    {        int p = Partition(a, lo, hi);   // the same Partition as above        if (p == target) return a[p];        if (p < target) lo = p + 1;     // the answer is on the right        else hi = p - 1;                // the answer is on the left    }} Console.WriteLine(KthSmallest([7, 2, 9, 4, 10, 1, 6, 3, 8, 5], 3));
Output
3
Quick check

After one call to Partition, what do you know for sure?

Quick check

Quick sort with “last item as pivot” is given an array that is already sorted. What happens?

Practice

An array contains only 0s, 1s and 2s (for example, status codes). Sort it in one pass with O(1) extra space: { 2, 0, 2, 1, 1, 0 } → 0 0 1 1 2 2. This is the “Dutch national flag” problem, a three-way partition.

Show a hint

Keep three regions with indexes low, mid, high: 0s before low, 1s between low and mid, 2s after high. Look at a[mid]: a 0 swaps to low (advance both), a 1 just advances mid, a 2 swaps to high (decrease high, do not advance mid).

Show the solution
Solution.cs
static void SortColors(int[] a){    int low = 0, mid = 0, high = a.Length - 1;    while (mid <= high)    {        if (a[mid] == 0)        {            (a[low], a[mid]) = (a[mid], a[low]);            low++; mid++;        }        else if (a[mid] == 1)        {            mid++;        }        else        {            (a[mid], a[high]) = (a[high], a[mid]);            high--;            // the swapped-in value is unchecked: keep mid        }    }} int[] a = { 2, 0, 2, 1, 1, 0 };SortColors(a);Console.WriteLine(string.Join(" ", a));
Output
0 0 1 1 2 2

Remember

  • Quick sort partitions around a pivot (smaller left, bigger right), then sorts each side.
  • Average O(n log n), in place, not stable; a bad pivot gives O(n²).
  • .NET uses introsort: quick sort + insertion sort for small pieces + heap sort as a safety net.
  • Quickselect finds the k-th smallest value in O(n) on average.
0 of 50 lessons completed