stable_partition 并获得 O(nlogn) 交换
Posted
技术标签:
【中文标题】stable_partition 并获得 O(nlogn) 交换【英文标题】:stable_partition and getting O(nlogn) swaps 【发布时间】:2018-09-16 15:34:17 【问题描述】:在en.cppreference.com 中,我们看到std::stable_partition
执行O(n) 交换,如果我们被允许使用额外的内存。这个,我看得出来。每次我们在谓词为假的范围内找到一个元素时,我们都会将其交换到另一个缓冲区中。最后,我们可以将这个额外的缓冲区复制到我们成功部分的末尾。 [我还假设,在这种情况下,stable_partition
可以仅使用 Forward 迭代器来实现]
我不明白的是,链接说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
项的谓词为假,n-p
项的谓词为真,我将得到 O((n - p) * p) 次交换。我想到了这一点,但我看不出我怎样才能得到最坏的情况 O(nlogn)。
LLVM 中的实现,我检查过但无法真正了解 O(nlogn) 交换是如何实现的。
PS:我的实现可能是错误的。我用几个输入测试了它,但就是这样。
【问题讨论】:
Comparison of sorting algorithms。O(n log n)
中有很多稳定且有效的算法
【参考方案1】:
递归思考。
如果左半边和右半边都是稳定分区的,如
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
应该返回什么。
【讨论】:
【参考方案2】:我不懂 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。
【讨论】:
谢谢,很明显它必须与稳定排序有关,但无法提出这样的关系。另外,我开始循环,认为稳定排序需要一个稳定的分区。 似乎这样的排序并不容易找到,但其中列出的一些算法符合所有条件:en.wikipedia.org/wiki/… 你不需要“排序”来做一个稳定的分区。您只需要一个临时缓冲区。您对元素进行 1-pass,如果谓词为假,则将元素移动到缓冲区,如果谓词为真,则覆盖元素。完成后,将临时缓冲区写回返回 true 的最后一个元素之后。工作完成 不使用缓冲区是问题的要求......它必须是没有额外内存的就地分区 DIci:任何自适应算法都不可能增加内存:stable_sort、stable_partition、inplace_merge。我似乎有复杂的算法试图管理这个。至少,您需要堆栈上的 1 个元素。其他一切都是通过轮换完成的。以上是关于stable_partition 并获得 O(nlogn) 交换的主要内容,如果未能解决你的问题,请参考以下文章
为forward_list实现stable_partition
STL 之 partition()方法 和 stable_partition()方法
蚂蚁集团获得Spider冠军,登上两项NL2SQL权威榜单榜首