14/11/2025
Quick Sort
Quick Sort is a divide-and-conquer sorting algorithm that works by selecting a pivot element, partitioning the array into elements smaller and larger than the pivot, and then recursively sorting the partitions. It is efficient on average with time complexity.
Quick Look:
- Time Complexity: Best/Average: Worst: (when pivot is always min/max)
- Space Complexity: (recursion stack)
- Stability: Unstable, consider an array of same elements, i pick the first one as partition. now it will be joined at the end. because, [<=partition]+[partition]+[>partition]. read more
Algorithm (Recursive Partitioning):
- Pick a pivot.
- Partition array: elements < pivot on left, > pivot on right.
- Recursively sort left and right subarrays.
code:
pythondef quick_sort(arr: list[int]) -> list[int]: if len(arr) <= 1: return arr pivot = arr[0] smaller_eq = [x for x in arr[1:] if x <= pivot] larger = [x for x in arr[1:] if x > pivot] return quick_sort(smaller_eq) + [pivot] + quick_sort(larger)def quick_sort(arr: list[int]) -> list[int]: if len(arr) <= 1: return arr pivot = arr[0] smaller_eq = [x for x in arr[1:] if x <= pivot] larger = [x for x in arr[1:] if x > pivot] return quick_sort(smaller_eq) + [pivot] + quick_sort(larger)
inplace version:
pythondef partition(arr, low, high): pi = high i = low - 1 # i points to the last index that was smaller than the pivot element for j in range(low, high): if arr[j] <= arr[pi]: i += 1 arr[i], arr[j] = arr[j], arr[i] # swap arr[i], arr[j] # why needed? dry run: [pi-x, pi-y, pi+a, pi-z, pi] arr[i+1], arr[pi] = arr[pi], arr[i+1] # swapping i+1 with pivot, # i <- last index that was smaller than pivot, so i+1 -> pivot's suitable position return i+1 # return pivots index def quicksort(arr, low, high): if low < high: pi = partition(arr, low, high) quicksort(arr, low, pi-1) quicksort(arr, pi+1, high)def partition(arr, low, high): pi = high i = low - 1 # i points to the last index that was smaller than the pivot element for j in range(low, high): if arr[j] <= arr[pi]: i += 1 arr[i], arr[j] = arr[j], arr[i] # swap arr[i], arr[j] # why needed? dry run: [pi-x, pi-y, pi+a, pi-z, pi] arr[i+1], arr[pi] = arr[pi], arr[i+1] # swapping i+1 with pivot, # i <- last index that was smaller than pivot, so i+1 -> pivot's suitable position return i+1 # return pivots index def quicksort(arr, low, high): if low < high: pi = partition(arr, low, high) quicksort(arr, low, pi-1) quicksort(arr, pi+1, high)
Visualisation, Sorting [7, 3, 5, 2, 9]:
shQUICK SORT VISUALIZATION ========================= Initial array: +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | <-- value +---+---+---+---+---+ 0 1 2 3 4 <-- index ======================================== PARTITION 1: Full array [7,3,5,2,9] ======================================== Range: left=0, right=4 Pivot: Choose last element = 9 (index 4) +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | ← pivot = 9 +---+---+---+---+---+ i=0 ^ pivot Goal: Move elements < 9 to left, >= 9 to right i = -1 (tracks position for next small element) j = 0 (scans through array) j=0: arr[0]=7 < 9 → increment i to 0, swap arr[0] with arr[0] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | +---+---+---+---+---+ i=0 ^ pivot j=1: arr[1]=3 < 9 → increment i to 1, swap arr[1] with arr[1] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | +---+---+---+---+---+ i=1 ^ pivot j=2: arr[2]=5 < 9 → increment i to 2, swap arr[2] with arr[2] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | +---+---+---+---+---+ i=2 ^ pivot j=3: arr[3]=2 < 9 → increment i to 3, swap arr[3] with arr[3] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | +---+---+---+---+---+ i=3 ^ pivot Place pivot: swap arr[i+1] with arr[4] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | ← 9 is now in correct position +---+---+---+---+---+ ^^^^^^^^^^^^^ ^ left part pivot Pivot index = 4 Left partition: [7,3,5,2] Right partition: [] (empty) ======================================== PARTITION 2: Left partition [7,3,5,2] ======================================== Range: left=0, right=3 Pivot: Choose last element = 2 (index 3) +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | ← pivot = 2 +---+---+---+---+---+ ^ ^ pivot i = -1, j = 0 j=0: arr[0]=7 > 2 → no swap j=1: arr[1]=3 > 2 → no swap j=2: arr[2]=5 > 2 → no swap Place pivot: swap arr[i+1=0] with arr[3] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← 2 is now in correct position +---+---+---+---+---+ ^ ^^^^^^^^^^^^ pivot right part Pivot index = 0 Left partition: [] (empty) Right partition: [3,5,7] ======================================== PARTITION 3: Right partition [3,5,7] ======================================== Range: left=1, right=3 Pivot: Choose last element = 7 (index 3) +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← pivot = 7 +---+---+---+---+---+ ^ ^ pivot i = 0, j = 1 j=1: arr[1]=3 < 7 → increment i to 1, swap arr[1] with arr[1] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | +---+---+---+---+---+ i=1 ^ pivot j=2: arr[2]=5 < 7 → increment i to 2, swap arr[2] with arr[2] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | +---+---+---+---+---+ i=2 ^ pivot Place pivot: swap arr[i+1=3] with arr[3] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← 7 is now in correct position +---+---+---+---+---+ ^^^^^^ ^ left part pivot Pivot index = 3 Left partition: [3,5] Right partition: [] (empty) ======================================== PARTITION 4: Left partition [3,5] ======================================== Range: left=1, right=2 Pivot: Choose last element = 5 (index 2) +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← pivot = 5 +---+---+---+---+---+ ^ ^ pivot i = 0, j = 1 j=1: arr[1]=3 < 5 → increment i to 1, swap arr[1] with arr[1] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | +---+---+---+---+---+ i=1 ^ pivot Place pivot: swap arr[i+1=2] with arr[2] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← 5 is now in correct position +---+---+---+---+---+ ^ ^ left pivot Pivot index = 2 Left partition: [3] Right partition: [] (empty) ======================================== BASE CASES ======================================== Partition [3]: size = 1 → already sorted All partitions complete! Finally sorted: ----------------- +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | +---+---+---+---+---+ Summary: -------- Time Complexity: - Average: O(n log n) - Worst: O(n²) when pivot is always smallest/largest Space Complexity: O(log n) for recursion stackQUICK SORT VISUALIZATION ========================= Initial array: +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | <-- value +---+---+---+---+---+ 0 1 2 3 4 <-- index ======================================== PARTITION 1: Full array [7,3,5,2,9] ======================================== Range: left=0, right=4 Pivot: Choose last element = 9 (index 4) +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | ← pivot = 9 +---+---+---+---+---+ i=0 ^ pivot Goal: Move elements < 9 to left, >= 9 to right i = -1 (tracks position for next small element) j = 0 (scans through array) j=0: arr[0]=7 < 9 → increment i to 0, swap arr[0] with arr[0] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | +---+---+---+---+---+ i=0 ^ pivot j=1: arr[1]=3 < 9 → increment i to 1, swap arr[1] with arr[1] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | +---+---+---+---+---+ i=1 ^ pivot j=2: arr[2]=5 < 9 → increment i to 2, swap arr[2] with arr[2] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | +---+---+---+---+---+ i=2 ^ pivot j=3: arr[3]=2 < 9 → increment i to 3, swap arr[3] with arr[3] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | +---+---+---+---+---+ i=3 ^ pivot Place pivot: swap arr[i+1] with arr[4] +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | ← 9 is now in correct position +---+---+---+---+---+ ^^^^^^^^^^^^^ ^ left part pivot Pivot index = 4 Left partition: [7,3,5,2] Right partition: [] (empty) ======================================== PARTITION 2: Left partition [7,3,5,2] ======================================== Range: left=0, right=3 Pivot: Choose last element = 2 (index 3) +---+---+---+---+---+ | 7 | 3 | 5 | 2 | 9 | ← pivot = 2 +---+---+---+---+---+ ^ ^ pivot i = -1, j = 0 j=0: arr[0]=7 > 2 → no swap j=1: arr[1]=3 > 2 → no swap j=2: arr[2]=5 > 2 → no swap Place pivot: swap arr[i+1=0] with arr[3] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← 2 is now in correct position +---+---+---+---+---+ ^ ^^^^^^^^^^^^ pivot right part Pivot index = 0 Left partition: [] (empty) Right partition: [3,5,7] ======================================== PARTITION 3: Right partition [3,5,7] ======================================== Range: left=1, right=3 Pivot: Choose last element = 7 (index 3) +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← pivot = 7 +---+---+---+---+---+ ^ ^ pivot i = 0, j = 1 j=1: arr[1]=3 < 7 → increment i to 1, swap arr[1] with arr[1] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | +---+---+---+---+---+ i=1 ^ pivot j=2: arr[2]=5 < 7 → increment i to 2, swap arr[2] with arr[2] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | +---+---+---+---+---+ i=2 ^ pivot Place pivot: swap arr[i+1=3] with arr[3] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← 7 is now in correct position +---+---+---+---+---+ ^^^^^^ ^ left part pivot Pivot index = 3 Left partition: [3,5] Right partition: [] (empty) ======================================== PARTITION 4: Left partition [3,5] ======================================== Range: left=1, right=2 Pivot: Choose last element = 5 (index 2) +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← pivot = 5 +---+---+---+---+---+ ^ ^ pivot i = 0, j = 1 j=1: arr[1]=3 < 5 → increment i to 1, swap arr[1] with arr[1] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | +---+---+---+---+---+ i=1 ^ pivot Place pivot: swap arr[i+1=2] with arr[2] +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | ← 5 is now in correct position +---+---+---+---+---+ ^ ^ left pivot Pivot index = 2 Left partition: [3] Right partition: [] (empty) ======================================== BASE CASES ======================================== Partition [3]: size = 1 → already sorted All partitions complete! Finally sorted: ----------------- +---+---+---+---+---+ | 2 | 3 | 5 | 7 | 9 | +---+---+---+---+---+ Summary: -------- Time Complexity: - Average: O(n log n) - Worst: O(n²) when pivot is always smallest/largest Space Complexity: O(log n) for recursion stack
Complexity Analysis:
In Quick Sort, we select a pivot element, partition the array around the pivot, and recursively sort the subarrays.
Recurrence Relation:
Best/Average Case:
Let be the time to sort an array of size .
- Selecting a pivot and partitioning the array takes time.
- In the best case, the pivot divides the array into two equal halves of size each.
- Recursively sorting each half takes time.
Therefore, the recurrence is:
with base case:
Solving using Master Theorem:
The recurrence is of the form:
where , , and .
We compute:
Since , we are in Case 2 of the Master Theorem.
Therefore:
Worst Case:
In the worst case, the pivot is always the smallest or largest element, resulting in one subarray of size and one of size .
The recurrence becomes:
with base case:
Expanding the recurrence:
For more animations, visit: https://dsa-experiments.vercel.app/recursion/quick-sort
Quicksort Algorithm Visualization
A step-by-step visualization of the quicksort partitioning process