Standard library partition algorithm

Your version is close to Nico Lomuto partition. Such partition works on ForwardIterators 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 BidirectionalIterators, 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 swaps at maximum, while Forward version can do up to ~N swaps.

std::partition in C++98/03 works on BidirectionalIterators, but in C++11 they relaxed requirements to ForwardIterators (though, it doesn't have to be semi-stable). Complexity requirements:

Complexity: If ForwardIterator meets the requirements for a BidirectionalIterator, at most (last -first) / 2 swaps are done; otherwise at most last - 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 ForwardIterators and Hoare's partition for BidirectionalIterators.

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 moves)


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.