Implementation of QuickSort to handle duplicates
The simple implementation idea is to separate the values into three groups: values less than the pivot, values equal to the pivot, and values greater than the pivot.

In pseudocode, the algorithm looks like the following.

algorithm quicksort(A, lo, hi):
    if lo &lt; hi then
        p ← pivot(A, lo, hi)
        left, right ← three-way-partition(A, p, lo, hi) 

        quicksort(A, lo, left - 1)
        quicksort(A, right, hi)


The partition procedure looks like the following.

procedure three-way-partition(A, pivot, lo, hi):
    l ← lo
    r ← lo
    u ← hi

    while r ≤ u:
        if A[r] &lt; pivot:
            swap A[l] and A[r]
            l ← l + 1
            r ← r + 1
        else if A[r] &gt; pivot:
            swap A[r] and A[u]
            u ← u - 1
        else: // the element is equal to pivot
            r ← r + 1
    return l, r


It uses three indices l, r and u (left, right, and upper bound), maintaining the following invariant in the while loop.


lo ≤ l ≤ r ≤ u ≤ hi
the elements with index in [lo, l) are smaller to the pivot. 
the elements with index in [l, r) are equal to the pivot. 
the elements with index in [r, u] are not examined yet. 
the elements with index in (u, hi] are greater than the pivot. 




There are a few minor variants. The above should be enough for you to understand what is going on.