Standard library partition algorithm
Your version is close to Nico Lomuto partition
. Such partition
works on ForwardIterator
s and is semi-stable (first part is stable, which can be useful in some circumstances).
Version from implementation of standard library which you quoted is close to partition
described by C. A. R. Hoare at his paper "Quicksort". It works on BidirectionalIterator
s, and does not imply any stability.
Let's compare them on following case:
FTTTT
Forward partition
will proceed like this:
FTTTT
TFTTT
TTFTT
TTTFT
TTTTF
resulting in swap
on each iteration except first, while Bidirectional partition will go thru following permutations:
FTTTT
TTTTF
resulting only in one swap
for all iterations.
Moreover, in general case Bidirectional will do N/2 swap
s at maximum, while Forward version can do up to ~N swap
s.
std::partition
in C++98/03 works on BidirectionalIterator
s, but in C++11 they relaxed requirements to ForwardIterator
s (though, it doesn't have to be semi-stable). Complexity requirements:
Complexity: If
ForwardIterator
meets the requirements for aBidirectionalIterator
, at most (last
-first
) / 2 swaps are done; otherwise at mostlast
-first
swaps are done. Exactly last - first applications of the predicate are done.
As you can see, implementations of standard library most likely will use Lomuto's partition
for ForwardIterator
s and Hoare's partition
for BidirectionalIterator
s.
Alexander Stepanov discuses partition
problem in his Notes on Programming and in Elements of Programming co-authored with Paul McJones
Live Demo
#include <initializer_list>
#include <forward_list>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <list>
using namespace std;
int counter = 0;
struct T
{
int value;
T(int x = 0) : value(x) {}
T(const T &x)
{
++counter;
value = x.value;
}
T &operator=(const T &x)
{
++counter;
value = x.value;
return *this;
}
};
auto pred = [](const T &x){return x.value;};
template<typename Container>
void test()
{
Container l = {0, 1, 1, 1, 1};
counter = 0;
partition(begin(l), end(l), pred);
cout << "Moves count: " << counter << endl;
}
int main()
{
test<forward_list<T>>();
test<list<T>>();
}
Output is:
Moves count: 12
Moves count: 3
(swap
is 3 move
s)
Your function has a serious defect. It swaps each element that satisfies the predicate with itself if initial elements of the sequence satisfy the predicate.