DocsHub
Sorting

Quick Sort

Learn how quick sort partitions around a pivot to sort in-place with average O(n log n) time.

Quick Sort

Quick sort picks a pivot element and partitions the array so everything smaller than the pivot goes left, everything larger goes right. The pivot is now in its final sorted position. Recursively sort the left and right partitions. No merging needed — the sorting happens in-place during partitioning.

Quick SortO(n log n) avg · partition
5
[0]
3
[1]
8
[2]
1
[3]
9
[4]
2
[5]
6
[6]
4
[7]
pivot
boundary (i)
scanner (j)
swapping
sorted
quick_sort.py
def quick_sort(arr, lo, hi):
pivot = arr[hi] # choose pivot
for j in range(lo, hi):
if arr[j] <= pivot: swap
place pivot at arr[i+1]
# base case — single element
return arr
Start quick sort — pivot partitions elements
step 0/44
0%

How it works

def quick_sort(arr, low=0, high=None):
    if high is None:
        high = len(arr) - 1

    if low < high:
        pivot_idx = partition(arr, low, high)
        quick_sort(arr, low, pivot_idx - 1)
        quick_sort(arr, pivot_idx + 1, high)

    return arr


def partition(arr, low, high):
    pivot = arr[high]   # choose last element as pivot
    i = low - 1         # index of smaller element

    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]

    # place pivot in its correct position
    arr[i + 1], arr[high] = arr[high], arr[i + 1]
    return i + 1

How the partition works

Partition [3, 6, 8, 10, 1, 2, 1], pivot = 1 (last element)

i = -1

j=0: arr[0]=3 > 1 — skip
j=1: arr[1]=6 > 1 — skip
j=2: arr[2]=8 > 1 — skip
j=3: arr[3]=10 > 1 — skip
j=4: arr[4]=1 ≤ 1 — i=0, swap arr[0] and arr[4]: [1, 6, 8, 10, 3, 2, 1]
j=5: arr[5]=2 > 1 — skip

Place pivot: swap arr[i+1]=arr[1] and arr[high]=arr[6]: [1, 1, 8, 10, 3, 2, 6]
pivot is now at index 1 — everything left is ≤ 1, everything right is > 1

Pivot selection matters

The pivot choice dramatically affects performance:

Last element (shown above) — simple, but O(n²) on already-sorted arrays. Every partition produces one empty subarray and one of size n-1 — n levels deep instead of log n.

Random pivot — pick a random element as pivot. O(n²) becomes extremely unlikely. The expected case is O(n log n).

Median-of-three — take the median of the first, middle, and last elements. Avoids sorted-array worst case with minimal overhead.

def partition_random(arr, low, high):
    import random
    rand_idx = random.randint(low, high)
    arr[rand_idx], arr[high] = arr[high], arr[rand_idx]
    return partition(arr, low, high)

Quick sort is often faster than merge sort in practice despite the same O(n log n) average — it has better cache locality (in-place) and lower constant factors. Python's sorted() uses Timsort, but most C++ and Java standard library sorts use quicksort variants (introsort) for in-place sorting.


Quick sort vs merge sort

Quick sortMerge sort
Time (average)O(n log n)O(n log n)
Time (worst)O(n²)O(n log n)
SpaceO(log n) call stackO(n)
In-placeYesNo
StableNoYes
Pivot dependentYesNo

Complexity

CaseTimeSpace
BestO(n log n)O(log n)
AverageO(n log n)O(log n)
Worst (sorted input, bad pivot)O(n²)O(n)

On this page