在en.cppreference.com中,如果允许我们使用额外的内存,我们将看到std::stable_partition
执行O(n)交换。我可以看到这一点。每当我们在谓词为false的范围内找到一个元素时,就会将其交换到另一个缓冲区中。最后,我们可以将这个额外的缓冲区复制到成功部分的末尾。 [我还假设在这种情况下,stable_partition
仅可以使用正向迭代器实现]
我没有得到的是,链接说stable_partition
最多执行O(nlogn)交换,前提是我们不允许使用额外的内存。这是我的尝试。
#include <utility>
namespace cho {
template <typename BidirIt, typename Pred>
BidirIt stable_partition(BidirIt begin, BidirIt end, Pred p) {
BidirIt next_p = begin;
for(BidirIt it = begin; it != end; ++it) {
if(it == begin && p(*it)) {
next_p++;
continue;
}
if(p(*it)) {
std::swap(*next_p++, *it);
// riplle back swapped element to keep stability
BidirIt cur = it;
do {
BidirIt prev = --cur; cur++;
std::swap(*cur--, *prev--);
} while(cur != next_p);
}
}
return next_p;
}
template <typename ForwardIt, typename Pred>
ForwardIt partition(ForwardIt begin, ForwardIt end, Pred p) {
ForwardIt next_p = begin;
for(ForwardIt it = begin; it != end; ++it) {
if(p(*it)) {
std::swap(*next_p++, *it);
}
}
return next_p;
}
}
在这种情况下,交换后我会回荡。因此,如果两个连续的真实案例之间的距离为k
,我将执行k
交换。我认为,对于我的算法,当范围被反向分区时,会发生最坏的情况。如果有p
个谓词为false的项目和n-p
个谓词为true的项目,我将获得O((n-p)* p)交换。我考虑了一下,但看不到如何得到最坏的情况O(nlogn)。
我检查了LLVM中的实现,但无法真正了解如何实现O(nlogn)交换。
PS:我的实现可能是错误的。我用几个输入测试了它,仅此而已。
答案 0 :(得分:3)
递归思考。
如果左半部分和右半部分都进行了稳定分区,如
0...01...10...01...1
b m e
剩下的唯一操作是旋转b, e
范围,将m
移到b
所在的位置。需要进行O(n)
交换。现在以递归的方式思考,稳定的分区将两半。将有O(log n)
个递归级别,总共O(n log n)
个交换。
iter stable_partition(begin, end) {
if (end - begin < 2)
return;
iter mid = begin + (end - begin) / 2;
iter left_break = stable_partition(begin, mid);
iter right_break = stable_partition(mid, end);
return rotate(left_break, mid, right_break);
}
当然,您必须仔细考虑rotate
应该返回什么。
答案 1 :(得分:1)
我不了解C ++,所以我将无法为您编写它,但是如果您有一个稳定的排序实现可用,这似乎很简单。嗯,由于您需要不使用任何额外的内存,因此该实现也需要就地排序。只要有这样的排序实现,只需按照以下顺序关系对元素进行排序:
R(x, y) = 0 if p(x) == p(y)
R(x, y) = -1 if p(x) && !p(y)
R(x, y) = 1 if !p(x) && p(y)
出于兴趣,哪种排序算法适合于此?事实证明,似乎没有太多人可以勾选所有方框,请参见here。