我写了这个分区函数:
template <class I, class P> I partition(I beg, I end, P p)
{
I first = beg;
while(beg != end) {
if(!p(*beg))
beg++;
else {
// if(beg != first) - EDIT: add conditional to prevent swapping identical elements
std::swap(*beg, *first);
first++;
beg++;
}
}
return first;
}
我用几个输出测试了它,我没有发现它有任何问题。
标准库分区功能相当于:
template <class BidirectionalIterator, class UnaryPredicate>
BidirectionalIterator partition (BidirectionalIterator first,
BidirectionalIterator last, UnaryPredicate pred)
{
while (first!=last) {
while (pred(*first)) {
++first;
if (first==last) return first;
}
do {
--last;
if (first==last) return first;
} while (!pred(*last));
swap (*first,*last);
++first;
}
return first;
}
后者似乎要复杂得多并且有嵌套循环。我的版本有问题吗?如果不是为什么更复杂的版本?
以下是使用以下谓词的输出:
bool greaterthantwo(double val)
{
return val > 2;
}
MAIN
std::vector<double> test{1,2,3,4,2,5,6,7,4,8,2,4,10};
std::vector<double>::iterator part = ::partition(test.begin(), test.end(), greaterthantwo);
for(const auto &ref:test)
std::cout << ref << " ";
std::cout << std::endl;
for(auto it = part; it != test.end(); it++)
std::cout << *it << " ";
std::cout << std::endl;
OUTPUT
3 4 5 6 7 4 8 4 10 2 2 2 1
2 2 2 1
答案 0 :(得分:6)
您的版本接近Nico Lomuto partition
。这样的partition
适用于ForwardIterator
s并且是半stable(第一部分是稳定的,在某些情况下可能很有用)。
您引用的标准库实现版本接近C. A. R. Hoare在他的论文“Quicksort”中描述的partition
。它适用于BidirectionalIterator
s,并不意味着任何稳定性。
让我们根据以下情况对它们进行比较:
FTTTT
转发partition
将如下进行:
FTTTT
TFTTT
TTFTT
TTTFT
TTTTF
在每次迭代时产生swap
,除了第一次,而双向分区将通过以下排列:
FTTTT
TTTTF
对于所有迭代只产生一个swap
。
此外,一般情况下,双向最多会执行N / 2 swap
s,而正向版最多可以执行~N swap
s。
std::partition
适用于BidirectionalIterator
s,但在C ++ 11中,它们放宽了ForwardIterator
的要求(但是,它不一定是半稳定)。复杂性要求:
复杂性:如果
ForwardIterator
符合BidirectionalIterator
的要求,则最多(last
-first
)/ 2次互换完成;否则最多last
-first
掉期就完成了。完全是最后 - 谓词的第一个应用程序已经完成。
正如您所看到的,标准库的实现很可能会使用Lomuto的partition
ForwardIterator
和Hoare partition
来BidirectionalIterator
。
Alexander Stepanov在与Notes on Programming共同撰写的Elements of Programming和Paul McJones中讨论了partition
个问题
#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>>();
}
输出是:
Moves count: 12
Moves count: 3
(swap
为3 move
s)
答案 1 :(得分:2)
您的功能存在严重缺陷。如果序列的初始元素满足谓词,它会自动交换满足谓词的每个元素。
答案 2 :(得分:1)
复杂性 第一个和最后一个之间的距离线性:对每个元素应用pred,并且可能交换其中的一些元素(如果迭代器类型是双向的,最多是交换的一半,否则最多那么多)。
在您的实施中,您可以进行更多交换。