简体   繁体   English

标准库分区算法

[英]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 a BidirectionalIterator , at most ( last - first ) / 2 swaps are done;复杂性:如果ForwardIterator满足BidirectionalIterator的要求,则最多( last - first )/ 2 次交换完成; otherwise at most last - 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问题


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

相关问题 标准库中有子序列算法吗? - Is there a subsequence algorithm in the Standard Library? 如何`std :: bind()`标准库算法? - How to `std::bind()` a standard library algorithm? 使用哪种算法在GNU C ++标准库中计算指数函数? - With which algorithm exponential functions are computed in the GNU C++ Standard Library? 将成员函数作为比较运算符传递给C ++标准库算法 - Pass a member function as Compare operator for C++ standard library algorithm 标准库算法来平均相邻元素之间的差异 - Standard library algorithm to average the difference between adjacent elements 快速排序分区算法 - Quicksort Partition Algorithm 快速排序分区算法 - Quick sort partition algorithm 标准算法库中的哪些算法分配,并且有一种方法可以指定这种分配的发生方式? - Which Algorithms in the standard algorithm library allocate and is there a way to specify how this allocation occurs? 为什么我在 C++ 中定义了“epsilon”后不能包含标准算法库? - Why can't I include the standard algorithm library after defining 'epsilon' in C++? 快速逆平方根算法是否比 C++ 的标准库 sqrt() function 更快? - Is the Fast Inverse Square Root Algorithm faster than C++'s standard library sqrt() function?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM