Home/Algorithms/Quick Sort Algorithm

Quick Sort Algorithm

šŸŽÆ Explain Like I'm 5: Sorting Your Toy Collection

Imagine you have a messy pile of toy cars, and you want to organize them by size from smallest to biggest!

šŸ§’ Kid's Way (Slow): Look at ALL the cars one by one, find the smallest, put it first. Then look at ALL remaining cars, find the smallest, put it second. Keep repeating... This takes FOREVER! 😓

šŸ§™ Quick Sort Magic (Fast):

  • 1. Pick one car as your 'pivot' (let's say the middle one)
  • 2. Put ALL smaller cars on the LEFT pile
  • 3. Put ALL bigger cars on the RIGHT pile
  • 4. Now do the SAME trick on the left pile and right pile!
  • 5. Keep doing this until all piles have just 1 car - DONE! šŸŽ‰

šŸš€ Why is it called 'Quick' Sort? Because you DON'T compare every car with every other car! You split the pile in half, then split those halves, then split those... It's like organizing by cutting the work in half each time! Much smarter!

How Quick Sort Works

Step 1: Choose a Pivot

Pick an element as the 'pivot' (usually the last, first, or middle element). This will be our reference point.

Step 2: Partition

Rearrange the array so all elements smaller than the pivot are on the left, and all larger elements are on the right. The pivot is now in its correct sorted position!

Step 3: Divide and Conquer

Recursively apply the same process to the left and right subarrays. Keep dividing until you have subarrays of size 1 (which are already sorted).

Step 4: Combine

No combining step needed! Once all recursive calls finish, the array is sorted in-place.

šŸ“ Visual Example

Let's sort the array [8, 3, 1, 7, 0, 10, 2]

Initial: [8, 3, 1, 7, 0, 10, 2] - Choose pivot = 2
Partition: [0, 1, 2, 7, 3, 10, 8] - 2 is now in correct position
Left: [0, 1] Right: [7, 3, 10, 8]
Continue recursively until sorted: [0, 1, 2, 3, 7, 8, 10] āœ“

ā±ļø Time & Space Complexity

Best Case:

O(n log n) - When pivot divides array evenly each time

Average Case:

O(n log n) - Most common scenario with random data

Worst Case:

O(n²) - When pivot is always smallest or largest (sorted array with bad pivot choice)

Space Complexity:

O(log n) - For recursive call stack (can be O(n) in worst case)

āœ… Advantages

  • āœ“Very fast in practice (usually faster than Merge Sort)
  • āœ“In-place sorting (uses very little extra memory)
  • āœ“Cache-friendly (good memory locality)
  • āœ“Easy to implement

āš ļø Disadvantages

  • āœ—Worst case is O(n²) - though rare with good pivot selection
  • āœ—Not stable (doesn't preserve relative order of equal elements)
  • āœ—Performance depends heavily on pivot choice
  • āœ—Recursive (can cause stack overflow with deep recursion)

When to Use Quick Sort?

  • →When average-case performance matters more than worst-case
  • →When you need in-place sorting with minimal extra space
  • →For large datasets in random order
  • →When you can use randomized pivot selection

When NOT to Use Quick Sort?

  • ⚠When you need guaranteed O(n log n) performance (use Merge Sort)
  • ⚠When stability is required (use Merge Sort)
  • ⚠For nearly sorted or reverse sorted data (without randomization)
  • ⚠When recursion depth is a concern (use Heap Sort)

šŸ’» Java Implementations

Implementation 1: Basic Quick Sort

Classic implementation with last element as pivot

Implementation 1: Basic Quick Sort
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class QuickSort {
// Main function to sort array using Quick Sort
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// Partition the array and get the pivot index
int pivotIndex = partition(arr, low, high);
// Recursively sort elements before and after partition
quickSort(arr, low, pivotIndex - 1); // Sort left subarray
quickSort(arr, pivotIndex + 1, high); // Sort right subarray
}
}
// Partition function - places pivot in correct position
private static int partition(int[] arr, int low, int high) {
// Choose the last element as pivot
int pivot = arr[high];
// Index of smaller element
int i = low - 1;
// Traverse through all elements
// If current element is smaller than pivot, swap it
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
// Swap arr[i] and arr[j]
swap(arr, i, j);
}
}
// Place pivot in correct position
swap(arr, i + 1, high);
return i + 1; // Return pivot index
}
// Helper function to swap two elements
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// Helper function to print array
private static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
// Test the Quick Sort implementation
public static void main(String[] args) {
int[] arr = {8, 3, 1, 7, 0, 10, 2};
System.out.println("Original array:");
printArray(arr);
quickSort(arr, 0, arr.length - 1);
System.out.println("Sorted array:");
printArray(arr);
}
}
/*
* How it works:
*
* Step 1: partition([8,3,1,7,0,10,2], 0, 6)
* pivot = 2
* After partition: [0,1,2,7,3,10,8]
* Return index: 2
*
* Step 2: Recursively sort left [0,1] and right [7,3,10,8]
*
* Left part: [0,1]
* pivot = 1
* Already sorted: [0,1]
*
* Right part: [7,3,10,8]
* pivot = 8
* After partition: [7,3,8,10]
* Continue recursively...
*
* Final result: [0,1,2,3,7,8,10]
*
* Time Complexity: O(n log n) average, O(n²) worst case
* Space Complexity: O(log n) for recursion stack
*/

Implementation 2: Randomized Quick Sort

Uses random pivot to avoid worst case on sorted data

Implementation 2: Randomized Quick Sort
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import java.util.Random;
public class RandomizedQuickSort {
private static Random random = new Random();
// Main function with randomized pivot
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// Use randomized partition instead of regular partition
int pivotIndex = randomizedPartition(arr, low, high);
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
}
}
// Randomized partition - randomly select pivot
private static int randomizedPartition(int[] arr, int low, int high) {
// Generate random index between low and high
int randomIndex = low + random.nextInt(high - low + 1);
// Swap random element with last element
swap(arr, randomIndex, high);
// Now use regular partition with randomized pivot
return partition(arr, low, high);
}
// Regular partition function (same as before)
private static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
swap(arr, i, j);
}
}
swap(arr, i + 1, high);
return i + 1;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
private static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
public static void main(String[] args) {
// Test with already sorted array (worst case for regular quick sort)
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println("Original array (sorted):");
printArray(arr);
quickSort(arr, 0, arr.length - 1);
System.out.println("After Quick Sort:");
printArray(arr);
System.out.println("\nNote: Randomized pivot helps avoid O(n²) on sorted arrays!");
}
}
/*
* Why Randomized Pivot?
*
* Problem with regular Quick Sort:
* - If array is already sorted (or reverse sorted)
* - And we always pick last element as pivot
* - We get worst case O(n²) performance!
*
* Example: [1,2,3,4,5] with last element pivot
* - Pivot = 5 → splits into [1,2,3,4] and []
* - Pivot = 4 → splits into [1,2,3] and []
* - This creates unbalanced partitions → O(n²)
*
* Solution: Random Pivot
* - Pick random element as pivot
* - Average case: balanced partitions
* - Expected time: O(n log n) even on sorted data!
*
* Trade-off: Adds randomness overhead, but avoids worst case
*/

Implementation 3: Three-Way Quick Sort

Optimized for arrays with many duplicate elements

Implementation 3: Three-Way Quick Sort
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public class ThreeWayQuickSort {
// Three-way Quick Sort - optimized for duplicate keys
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// Partition into three parts: < pivot, = pivot, > pivot
int[] pivots = threeWayPartition(arr, low, high);
int lt = pivots[0]; // Last index of elements < pivot
int gt = pivots[1]; // First index of elements > pivot
// Recursively sort elements less than and greater than pivot
// Elements equal to pivot are already in correct position!
quickSort(arr, low, lt);
quickSort(arr, gt, high);
}
}
// Three-way partition: returns [lt, gt]
// lt: last index of elements < pivot
// gt: first index of elements > pivot
private static int[] threeWayPartition(int[] arr, int low, int high) {
int pivot = arr[low]; // Choose first element as pivot
int lt = low; // Elements before lt are < pivot
int i = low + 1; // Current element being examined
int gt = high; // Elements after gt are > pivot
while (i <= gt) {
if (arr[i] < pivot) {
// Element is less than pivot - move to left section
swap(arr, lt, i);
lt++;
i++;
} else if (arr[i] > pivot) {
// Element is greater than pivot - move to right section
swap(arr, i, gt);
gt--;
// Don't increment i - need to examine swapped element
} else {
// Element equals pivot - leave it in middle
i++;
}
}
return new int[]{lt - 1, gt + 1};
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
private static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
public static void main(String[] args) {
// Array with many duplicates
int[] arr = {4, 2, 7, 2, 4, 1, 7, 4, 2, 7};
System.out.println("Original array (with duplicates):");
printArray(arr);
quickSort(arr, 0, arr.length - 1);
System.out.println("Sorted array:");
printArray(arr);
}
}
/*
* Three-Way Partitioning Visualization:
*
* Array: [4, 2, 7, 2, 4, 1, 7, 4, 2, 7]
* Pivot: 4 (first element)
*
* After partition:
* [2, 2, 1, 2] [4, 4, 4] [7, 7, 7]
* < pivot = pivot > pivot
*
* Advantages for duplicate keys:
* 1. Elements equal to pivot don't need further sorting
* 2. Reduces number of recursive calls
* 3. Much faster than regular Quick Sort for arrays with duplicates
*
* Example use case:
* - Sorting colors: [Red, Blue, Red, Green, Blue, Red, Green]
* - All Reds grouped together after one partition!
*
* Time Complexity:
* - Best/Average: O(n log n)
* - Worst: O(n²) but rare
* - Optimal: O(n) when all elements are equal!
*/

šŸ”‘ Key Points to Remember

  • →Quick Sort is a DIVIDE AND CONQUER algorithm
  • →The partition step is the most important - it places pivot in correct position
  • →Average case O(n log n) makes it one of the fastest sorting algorithms
  • →Randomized pivot selection helps avoid worst case O(n²)
  • →In-place sorting makes it memory efficient

šŸŽÆ Optimization Techniques

  • šŸ’”Randomized Pivot: Choose random element to avoid worst case
  • šŸ’”Median-of-Three: Use median of first, middle, and last elements as pivot
  • šŸ’”Insertion Sort for Small Arrays: Switch to insertion sort when subarray size < 10
  • šŸ’”Three-Way Partitioning: Handle duplicate keys efficiently
  • šŸ’”Iterative Version: Use stack instead of recursion to avoid stack overflow

šŸ†š Quick Sort vs Other Sorting Algorithms

Quick Sort vs Merge Sort:

Quick Sort is usually faster in practice and uses less space, but Merge Sort guarantees O(n log n) and is stable.

Quick Sort vs Heap Sort:

Quick Sort is faster on average due to better cache locality, but Heap Sort guarantees O(n log n) and is in-place.

Quick Sort vs Insertion Sort:

Quick Sort is much faster for large arrays, but Insertion Sort is better for small arrays (< 10 elements) and nearly sorted data.

šŸ’” Interview Tips

  • šŸ’”Always mention time complexity: Average O(n log n), Worst O(n²)
  • šŸ’”Explain why randomized pivot helps avoid worst case
  • šŸ’”Discuss the trade-off: Fast but not stable
  • šŸ’”Know when to use Quick Sort vs Merge Sort vs Heap Sort
  • šŸ’”Be able to implement both recursive and iterative versions

šŸ’Ŗ Practice Problems

  • •Sort an array of integers using Quick Sort
  • •Find the Kth largest element using Quick Select (Quick Sort variant)
  • •Sort an array of 0s, 1s, and 2s (Dutch National Flag - Three-Way Partition)
  • •Sort colors - same as above but with Red, White, Blue
  • •Implement Quick Sort iteratively using a stack