Quick Sort is one of the most important and widely used sorting algorithms.
It’s fast, elegant, and a favorite in interviews — if you understand it properly.
This post explains Quick Sort from basics to advanced, covering:
- Simple (functional) Quick Sort
- In-place Quick Sort
- Lomuto partition scheme
- Hoare partition scheme
- Pros & Cons
- Time and space complexity
What is Quick Sort?
Quick Sort is a divide-and-conquer algorithm.
The idea is simple:
- Pick a pivot element
- Partition the array so:
- elements smaller than pivot go left
- elements larger than pivot go right
- Recursively apply the same logic to left and right sub-arrays
“Quick Sort is quick because it reduces the problem size drastically at every step.”
Simple (Functional) Quick Sort
Let’s start with the most intuitive version.
const quickSort = (arr) => {
if (arr.length <= 1) {
return arr;
}
let pivot = arr[0];
let left = [];
let right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
};
How this works
- Base case: arrays of size 0 or 1 are already sorted
- Pivot chosen as the first element
- Remaining elements are split into left and right
- Recursively sort both halves
- Combine results
Pros
- Very easy to read and explain
- Great for learning and interviews
Cons
- ❌ Not in-place
- ❌ Uses extra memory
- ❌ Worst-case O(n²) when pivot choice is bad
In-place Quick Sort
In-place Quick Sort:
- Does not create extra arrays
- Uses indexes and swaps
- Much more space-efficient
- Preferred in real systems and interviews
The key to in-place Quick Sort is the partitioning step.
Lomuto Partition Scheme
Idea
- Pivot = last element
- i keeps track of the “smaller than pivot” region
- j scans the array
Code
const lomutoPartition = (arr, low, high) => {
let pivot = arr[high];
let i = low - 1;
for (let j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
[arr[i], arr[j]] = [arr[j], arr[i]];
}
}
[arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
return i + 1;
};
What happens here
Before:
[34, 56, 3, 234, 6, 7] pivot = 7
After partition:
[3, 6 | 7 | 56, 234, 34]
The pivot (7) is now in its final sorted position.
Quick Sort using Lomuto
const quickSort = (arr, low = 0, high = arr.length - 1) => {
if (low < high) {
const pivot = lomutoPartition(arr, low, high);
quickSort(arr, low, pivot - 1);
quickSort(arr, pivot + 1, high);
}
return arr;
};
let arr = [34, 56, 3, 234, 6, 7];
quickSort(arr);
Lomuto — Pros & Cons
Pros
- Simple and easy to implement
- Pivot ends in correct sorted position
- Excellent for interviews
Cons
- More swaps
- Performs poorly with many duplicates
- Slightly slower than Hoare
Hoare Partition Scheme
Idea
- Pivot = first element
- Two pointers:
- i moves from left
- j moves from right
- Swap elements on the wrong side
Code
const hoarePartition = (arr, low, high) => {
let pivot = arr[low];
let i = low - 1;
let j = high + 1;
while (true) {
do {
i++;
} while (arr[i] < pivot);
do {
j--;
} while (arr[j] > pivot);
if (i >= j) return j;
[arr[i], arr[j]] = [arr[j], arr[i]];
}
};
What happens here
Before:
[34, 56, 3, 234, 6, 7] pivot = 34
After partition:
[ 7, 6, 3 | 234, 56, 34 ]
Then it returns the partition index = 2.
Quick Sort using Hoare
const quickSortHoare = (arr, low = 0, high = arr.length - 1) => {
if (low < high) {
const p = hoarePartition(arr, low, high);
quickSortHoare(arr, low, p);
quickSortHoare(arr, p + 1, high);
}
return arr;
};
Lomuto vs Hoare
Lomuto:
- Pivot goes to its final sorted position
- Everything left < pivot, everything right > pivot
- Returns pivot index
Hoare:
- Pivot does NOT move to final position
- It only splits the array into two valid halves
- Returns a partition index, not pivot index
- Fewer swaps → faster
Hoare says:
“I don’t care where pivot ends up — I only ensure left side ≤ pivot and right side ≥ pivot.”
Why recursive calls look different from Lomuto ?
// lomuto
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
// hoare
quickSort(arr, low, partitionIndex);
quickSort(arr, partitionIndex + 1, high);
Because:
- Lomuto returns pivot index
- Hoare returns boundary index
Summary:
| Feature | Lomuto | Hoare |
|---|---|---|
| Simplicity | ⭐⭐⭐⭐ | ⭐⭐ |
| Swaps | More | Fewer |
| Speed | Slower | Faster |
| Pivot placement | Exact index | Not guaranteed |
| Duplicate handling | Poor | Better |
Time and Space Complexity
Time Complexity
| Case | Complexity |
|---|---|
| Best | O(n log n) |
| Average | O(n log n) |
| Worst | O(n²) |
Worst case occurs when pivot selection is poor (already sorted array).
Space Complexity
- Functional Quick Sort → O(n)
- In-place Quick Sort → O(log n) (recursion stack)
Top comments (0)