Quicksort
Hoare's partitioning
duplicate values
sorting algorithms
computer science

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.

Practice algorithms

Introduction

Quicksort is one of the most efficient and widely used sorting algorithms, mainly due to its average-case O(nlogn)O(n \log n) 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

  1. Select Pivot: Typically, the first element of the array is chosen as the pivot.
  2. Initialize Pointers: Have two pointers, `i` starting just before the beginning of the array and `j` starting just after the end of the array.
  3. 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`.
  4. 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 O(n2)O(n^2) 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:

AttributeHoare's PartitioningLomuto's Partitioning
Pivot ChoiceTypically first elementTypically last element
Number of ScansTwo pointers (left & right)Single loop
Number of SwapsFewer swapsMore swaps
Handling DuplicatesNaturally more efficientCan lead to skewed partitions
IntuitivenessLess intuitive initiallySimpler 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
Course
Intermediate
27 lessons
15 hours
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 course
Track 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.

Practice algorithms

All Rights Reserved.