I tried to search on Web and in my algorithms book
if the Lomuto's specific solution of QSort Partition is stable or not (I know that the Hoare's version is unstable)
but i didn't find a precise answer.
So I've tried to make same examples and it seems stable. But I didn't demonstrate it.
Could you help me?
If it is not stable, could you find me an example of input?

- 16,221
- 6
- 44
- 59

- 603
- 1
- 6
- 14
2 Answers
I'm going to interpret "Quicksort with Lomuto's partition" as referring to the algorithm from here (slides 21–22).
This algorithm is unstable on the array [a, b, c] where c < a = b.
I found this counterexample by implementing the Quicksort algorithm in Python so that (like Python's built-in sort) it takes a key
function. By supplying an appropriate key function, I can make the sort think that certain elements are identical, but I can still distinguish them. Then it's just a matter of trying lots of permutations and spotting instability. The code below certainly doesn't exhaust the possible tests (one might want to try more than two identical elements, or multiple sets of identical elements), but it was good enough in this case.
def lomuto(A, key=lambda x:x):
def partition(A, p, r):
i = p - 1
pivot = A[r]
for j in range(p, r):
if key(A[j]) <= key(pivot):
i += 1
A[i], A[j] = A[j], A[i]
A[i+1], A[r] = A[r], A[i+1]
return i + 1
def quicksort(A, p, r):
if p < r:
q = partition(A, p, r)
quicksort(A, p, q-1)
quicksort(A, q+1, r)
quicksort(A, 0, len(A) - 1)
def test_stability(f, n):
"""Try to discover if the sorting function f is stable on n inputs;
printing the first counterexample found, if any."""
import itertools
for i in range(n - 1):
def order(P): return P.index((i, 0)) < P.index((i, 1))
array = [(j, 0) for j in range(n - 1)] + [(i, 1)]
for P in map(list, itertools.permutations(array)):
Q = P[:] # take a copy
f(Q, key=lambda x: x[0])
if order(P) != order(Q):
print(P, '->', Q)
return
>>> test_stability(lomuto, 3)
[(1, 0), (1, 1), (0, 0)] -> [(0, 0), (1, 1), (1, 0)]

- 64,967
- 9
- 133
- 163
-
You're welcome. You might want to have a think about how I discovered this example... – Gareth Rees Jul 10 '11 at 14:18
-
What exactly is`lomuto` in the code above? (I don't see it defined). It seems to be the function that does the partitioning, but it needs to accept `key`? – Josh Apr 28 '20 at 16:58
-
@Josh: It's an implementation of Quicksort using Lomuto's partition, based on the slides I linked to. It's easy to write your own! – Gareth Rees Apr 28 '20 at 17:06
-
Also, in the example `[a,b,c]` with `c < a =b` wouldn't that simply advance the index keeping track of the end of the larger set of elements forward (and then moving the pivot to the beginning) without getting `a` and `b` out of order?? – Josh Apr 28 '20 at 17:07
-
Thanks @GarethRees I agree and appreciate your rapid answer. I am doing that myself now. – Josh Apr 28 '20 at 17:07
-
Leaving a note here for future reference: [This answer](https://stackoverflow.com/a/10375393/283296) illustrates with an example the precise moment that Lomuto's partitioning algorithm would leave identical elements in the input out of order in the output. – Josh Apr 28 '20 at 22:35
It depends by the efficiency.
Here comes the pseudocode from Wikipedia.
algorithm quicksort(A, lo, hi) is
if lo < hi then
p := partition(A, lo, hi)
quicksort(A, lo, p - 1)
quicksort(A, p + 1, hi)
algorithm partition(A, lo, hi) is
pivot := A[hi]
i := lo
for j := lo to hi do
if A[j] < pivot then
swap A[i] with A[j]
i := i + 1
swap A[i] with A[hi]
return i
And here comes an implementation in Java.
public static <E> void lomuto(final List<E> list, final Comparator<? super E> comparator) {
LOMUTO_SWAP_COUNTER.remove();
LOMUTO_SWAP_COUNTER.set(new LongAdder());
sort(list,
comparator,
(l, c) -> {
assert !l.isEmpty();
final int p = l.size() - 1;
int i = 0;
for (int j = 0; j < l.size() - 1; j++) {
if (c.compare(l.get(j), l.get(p)) < 0) { // < vs <=
swap(l, j, i++);
LOMUTO_SWAP_COUNTER.get().increment();
}
}
swap(l, p, i);
return i;
});
}
With following data,
[Three(3), John(2), Jane(2), One(1)] // original unsorted
Above implementation swaps 2
times with unstable output.
[One(1), Jane(2), John(2), Three(3)] // unstable, with 2 swaps
When you change c.compare(l.get(j), l.get(p)) < 0
with c.compare(l.get(j), l.get(p)) <= 0
,
The implementation swaps 3
times with stable output.
[One(1), John(2), Jane(2), Three(3)] // stable, with 3 swaps

- 20,295
- 14
- 115
- 184