[英]Standard library partition algorithm
I wrote this partition function:我写了这个分区函数:
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;
}
I've tested it with a few outputs and I haven't found anything wrong with it.我已经用一些输出对其进行了测试,但没有发现任何问题。
The standard library partition function is equivalent to:标准库分区函数等价于:
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;
}
The latter seems much more complicated and has nested loops.后者似乎要复杂得多,并且有嵌套循环。 Is there something wrong with my version?我的版本有问题吗? If not why the more complicated version?如果不是为什么更复杂的版本?
Here is some output using the following predicate:这是使用以下谓词的一些输出:
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
Your version is close to Nico Lomuto partition
.你的版本接近 Nico Lomuto partition
。 Such partition
works on ForwardIterator
s and is semi- stable (first part is stable, which can be useful in some circumstances).这种partition
适用于ForwardIterator
并且是半稳定的(第一部分是稳定的,这在某些情况下很有用)。
Version from implementation of standard library which you quoted is close to partition
described by CAR Hoare at his paper "Quicksort".您引用的标准库实现版本接近于CAR Hoare在他的论文“Quicksort”中描述的partition
。 It works on BidirectionalIterator
s, and does not imply any stability.它适用于BidirectionalIterator
s,并不意味着任何稳定性。
Let's compare them on following case:让我们在以下案例中比较它们:
FTTTT
Forward partition
will proceed like this:前向partition
将像这样进行:
FTTTT
TFTTT
TTFTT
TTTFT
TTTTF
resulting in swap
on each iteration except first, while Bidirectional partition will go thru following permutations:除了第一次之外,每次迭代都会导致swap
,而双向分区将通过以下排列:
FTTTT
TTTTF
resulting only in one swap
for all iterations.导致所有迭代只进行一次swap
。
Moreover, in general case Bidirectional will do N/2 swap
s at maximum, while Forward version can do up to ~N swap
s.此外,在一般情况下,双向最多可以执行 N/2 个swap
,而正向版本最多可以执行 ~N 个swap
。
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). C++98/03 中的std::partition
适用于BidirectionalIterator
s,但在 C++11 中,他们放宽了对ForwardIterator
s 的要求(尽管,它不必是半稳定的)。 Complexity requirements:复杂度要求:
Complexity: If
ForwardIterator
meets the requirements for aBidirectionalIterator
, at most (last
-first
) / 2 swaps are done;复杂性:如果ForwardIterator
满足BidirectionalIterator
的要求,则最多(last
-first
)/ 2 次交换完成; otherwise at mostlast
-first
swaps are done.否则至多last
-first
交换完成。 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.正如您所看到的,标准库的实现很可能将 Lomuto 的partition
用于ForwardIterator
s,Hoare 的partition
用于BidirectionalIterator
s。
Alexander Stepanov discuses partition
problem in his Notes on Programming and in Elements of Programming co-authored with Paul McJones Alexander Stepanov在他的Notes on Programming和与Paul McJones合着的Elements of Programming中讨论了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>>();
}
Output is:输出是:
Moves count: 12
Moves count: 3
( swap
is 3 move
s) ( swap
是 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.如果序列的初始元素满足谓词,则它将满足谓词的每个元素与其自身交换。
From STL partition description来自STL 分区说明
Complexity Linear in the distance between first and last: Applies pred to each element, and possibly swaps some of them (if the iterator type is a bidirectional, at most half that many swaps, otherwise at most that many).复杂性在第一个和最后一个之间的距离中呈线性:将 pred 应用于每个元素,并可能交换其中的一些(如果迭代器类型是双向的,最多交换一半,否则最多交换)。
In your implementation you swap more.在您的实现中,您交换了更多。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.