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]
ā±ļø 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
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
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
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