by, Subhajit Gorai
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 O(nlogn)O(n\log n) time complexity.

Quick Look:

  1. Time Complexity: Best/Average: O(nlogn)O(n \log n) Worst: O(n2)O(n^2) (when pivot is always min/max)
  2. Space Complexity: O(logn)O(\log n) (recursion stack)
  3. 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):

  1. Pick a pivot.
  2. Partition array: elements < pivot on left, > pivot on right.
  3. Recursively sort left and right subarrays.

code:

python
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:

python
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]:

sh
QUICK 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() 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 T(n)T(n) be the time to sort an array of size nn.

  • Selecting a pivot and partitioning the array takes Θ(n)\Theta(n) time.
  • In the best case, the pivot divides the array into two equal halves of size n/2n/2 each.
  • Recursively sorting each half takes T(n/2)T(n/2) time.

Therefore, the recurrence is: T(n)=2T(n/2)+Θ(n)T(n) = 2T(n/2) + \Theta(n)
with base case: T(1)=Θ(1)T(1) = \Theta(1)

Solving using Master Theorem:
The recurrence is of the form: T(n)=aT(n/b)+f(n)T(n) = aT(n/b) + f(n)
where a=2a = 2, b=2b = 2, and f(n)=Θ(n)f(n) = \Theta(n).

We compute: nlogba=nlog22=n1=nn^{\log_b a} = n^{\log_2 2} = n^1 = n

Since f(n)=Θ(n)=Θ(nlogba)f(n) = \Theta(n) = \Theta(n^{\log_b a}), we are in Case 2 of the Master Theorem.

Therefore: T(n)=Θ(nlogbalogn)=Θ(nlogn)T(n) = \Theta(n^{\log_b a} \log n) = \boxed{\Theta(n \log n)}

Worst Case:
In the worst case, the pivot is always the smallest or largest element, resulting in one subarray of size n1n-1 and one of size 00.

The recurrence becomes: T(n)=T(n1)+Θ(n)T(n) = T(n-1) + \Theta(n)
with base case: T(1)=Θ(1)T(1) = \Theta(1)

Expanding the recurrence:
T(n)=T(n1)+Θ(n)=T(n2)+Θ(n1)+Θ(n)==Θ(1+2++n)=Θ(n2)T(n) = T(n-1) + \Theta(n) = T(n-2) + \Theta(n-1) + \Theta(n) = \dots = \Theta(1 + 2 + \dots + n) = \boxed{\Theta(n^2)}

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

+
+
+
+