Quick Sort
Table of Contents
Introduction
Quick Sort is a divide-and-conquer sorting algorithm that works by picking a “pivot” element and partitioning the array so that everything smaller goes to the left and everything larger goes to the right. Then it recursively sorts each side. It’s like organizing a messy bookshelf by picking a book in the middle, putting everything shorter to the left and taller to the right, then repeating for each side.
Quick Sort is one of the most widely used sorting algorithms in practice — it’s the default in many standard library implementations because of its excellent average-case performance and low overhead. You’ll want to be familiar with Recursion, Arrays, and Big-O Notation before diving in.
How Quick Sort works
- Pick a pivot element from the array
- Partition: rearrange elements so values ≤ pivot are on the left, values > pivot are on the right
- Recurse on the left and right subarrays
- Base case: arrays of length 0 or 1 are already sorted
Visual example
[8, 3, 1, 7, 0, 10, 2] pivot = 7
Partition around 7:
[3, 1, 0, 2] 7 [8, 10]
↓ ↓
[1, 0, 2] 3 [] [8] 10 []
↓
[0] 1 [2]
Result: [0, 1, 2, 3, 7, 8, 10]
Implementation
Lomuto partition scheme
The simpler partition approach — uses the last element as pivot and maintains a boundary index.
Hoare partition scheme
The original partition algorithm by Tony Hoare. Uses two pointers that move toward each other. It’s more efficient in practice — fewer swaps on average.
pivotIndex (not pivotIndex - 1) for the left half.
The pivot problem
Quick Sort’s performance depends entirely on pivot selection. A bad pivot creates unbalanced partitions.
Worst case: already sorted input
If you always pick the last element as pivot on a sorted array:
[1, 2, 3, 4, 5] pivot = 5
[1, 2, 3, 4] 5 [] ← one side is empty every time
This gives O(n²) — each partition only removes one element.
Pivot strategies
Last element (Lomuto default) — simple but vulnerable to sorted input.
Middle element (Hoare default) — avoids the sorted-input worst case.
Median-of-three — pick the median of the first, middle, and last elements. Good balance of simplicity and performance:
function medianOfThree(arr, low, high) {
const mid = Math.floor((low + high) / 2);
if (arr[low] > arr[mid]) [arr[low], arr[mid]] = [arr[mid], arr[low]];
if (arr[low] > arr[high]) [arr[low], arr[high]] = [arr[high], arr[low]];
if (arr[mid] > arr[high]) [arr[mid], arr[high]] = [arr[high], arr[mid]];
// Place pivot at high - 1
[arr[mid], arr[high - 1]] = [arr[high - 1], arr[mid]];
return arr[high - 1];
}
Random pivot — pick a random element. Makes worst case extremely unlikely regardless of input:
function randomizedPartition(arr, low, high) {
const randomIndex = Math.floor(Math.random() * (high - low + 1)) + low;
[arr[randomIndex], arr[high]] = [arr[high], arr[randomIndex]];
return partition(arr, low, high); // standard Lomuto partition
}
Complexity analysis
| Case | Time | When |
|---|---|---|
| Best | O(n log n) | Pivot always splits evenly |
| Average | O(n log n) | Random or good pivot selection |
| Worst | O(n²) | Pivot is always min or max |
Space: O(log n) average for the recursion stack, O(n) worst case.
Why O(n log n) average? Each good partition splits the array roughly in half, giving log n levels of recursion. Each level does O(n) work scanning and swapping. So: n × log n.
Why O(n²) worst case? If every partition puts all elements on one side, you get n levels instead of log n. Each level still does O(n) work: n × n.
Quick Sort vs Merge Sort
| Quick Sort | Merge Sort | |
|---|---|---|
| Average time | O(n log n) | O(n log n) |
| Worst time | O(n²) | O(n log n) |
| Space | O(log n) in-place | O(n) extra |
| Stable | No | Yes |
| Cache performance | Better (in-place) | Worse (copies arrays) |
| Practical speed | Usually faster | More predictable |
Quick Sort wins in practice for most cases because it sorts in-place (better cache locality) and has lower constant factors. Merge Sort is preferred when you need stability or guaranteed O(n log n) worst case.
Interview tips
- Know both Lomuto and Hoare partitions — interviewers may ask you to explain the difference
- Be ready to explain the worst case and how to avoid it (randomized pivot)
- Quick Select (finding the k-th smallest element) uses the same partition logic — it’s a common follow-up question
- If asked “which sort would you use?”, Quick Sort is a strong default answer for in-memory sorting of primitives
Practice problems
- Sort an array (implement Quick Sort from scratch)
- Kth Largest Element in an Array (Quick Select)
- Sort Colors (Dutch National Flag — related partition logic)
- Top K Frequent Elements
- Wiggle Sort II
Quick Sort’s partition operation is one of the most useful building blocks in algorithm design. Once you understand how partitioning works, problems like Quick Select, Dutch National Flag, and three-way partitioning become much more approachable.