Quicksort - Hoare's partitioning with duplicate values
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Quicksort is one of the most efficient and widely used sorting algorithms, mainly due to its average-case time complexity and its in-place sort capabilities. It was invented by Tony Hoare in 1960. A core principle of Quicksort is its partitioning step, where it rearranges elements in such a way that elements less than a chosen pivot are on one side, and those greater are on the other. Among various partitioning schemes, Hoare's partitioning method stands out due to its efficiency and elegance. This article delves into Hoare's partitioning, focusing on handling duplicate values effectively.
Hoare's Partitioning Scheme
Hoare's partitioning technique is a classic method of partitioning used in Quicksort. It selects a pivot and rearranges the array such that all elements less than the pivot come before it, and all elements greater go after it. This allows the pivot to end up in its final sorted position in the array.
Hoare's Partitioning Steps
- Select Pivot: Typically, the first element of the array is chosen as the pivot.
- Initialize Pointers: Have two pointers, `i` starting just before the beginning of the array and `j` starting just after the end of the array.
- Partitioning:
- Increment `i` until an element greater than or equal to the pivot is found.
- Decrement `j` until an element less than or equal to the pivot is found.
- If `i` is less than `j`, swap the elements at `i` and `j`.
- Termination: The process continues until `i` is no longer less than `j`.
Example
Consider the following array: `[3, 9, 8, 4, 1, 7, 0, 6, 2, 5]` with the pivot as `3`:
- Initialize `i = -1` and `j = 10`
- Increment `i` to `3` and find `4` (greater than pivot), and decrement `j` to `6` and find `0` (less than pivot), swap them. Array becomes: `[3, 9, 8, 0, 1, 7, 4, 6, 2, 5]`.
- Now increment `i` twice to `2` (value `8`), decrement `j` to `5` (value `4`) and swap them: `[3, 9, 2, 0, 1, 7, 4, 6, 8, 5]`.
- Continue this process until `i` equals `j`, terminating the partitioning.
Handling Duplicates
Duplicate values present a challenge for partitioning schemes, potentially leading to inefficient sorts if not managed correctly. Given that Hoare's partitioning tends to handle duplicates inherently, a few adjustments ensure optimal performance:
- Partition Consistently: Ensure that values equal to the pivot get swapped correctly to maintain efficiency.
- Balanced Subarrays: Hoare's method tends to create more balanced partitions with duplicates than other methods, minimizing worst-case behavior.
Consider an array with duplicates: `[4, 5, 4, 3, 4, 2, 4, 1]`.
- With pivot `4`, Hoare's scheme would naturally distribute `4`s among both subarrays, enhancing performance.
Comparison with Lomuto's Partitioning
While Hoare's partitioning provides better efficiency in terms of balanced partitions with fewer swaps, it is occasionally considered less intuitive compared to Lomuto's partitioning scheme. Lomuto simpler in implementation but often performs more swaps and better caters to educational purposes than practical deployments. Here is a comparison of key aspects:
| Attribute | Hoare's Partitioning | Lomuto's Partitioning |
| Pivot Choice | Typically first element | Typically last element |
| Number of Scans | Two pointers (left & right) | Single loop |
| Number of Swaps | Fewer swaps | More swaps |
| Handling Duplicates | Naturally more efficient | Can lead to skewed partitions |
| Intuitiveness | Less intuitive initially | Simpler and intuitive |
Best Practices
When implementing Quicksort with Hoare's partitioning:
- Pivot Selection: Choose different pivot strategies (e.g., median of three, random pivot) to enhance performance across diverse datasets.
- Base Case Handling: Ensure small arrays are handled with simpler sorts like insertion sort to minimize overhead.
- Tail Recursive Optimization: Prioritize the recursive call with the smallest partition to minimize stack depth; alternatively, use iterative loops to simulate recursion.
Conclusion
Quicksort remains a foundational algorithm in computer science and software development. Hoare's partitioning, especially adept at managing duplicates and ensuring balanced subarrays, enhances its efficiency. By understanding its mechanics, adapting to specific datasets, and applying best practices, developers can leverage Quicksort for optimal performance in sorting tasks.
Related reading
- Quicksort - which sub-part should be sorted first?
- quicksort algorithm stability
- Quicksort Choosing the pivot
- Quicksort complexity when all the elements are same?
- Quicksort Iterative or Recursive
- Quicksort pivot position after one partition
- Quicksort slower than Mergesort?
- Quicksort superiority over Heap Sort

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.