代码之家  ›  专栏  ›  技术社区  ›  meguli

稳定分区并获得O(nlogn)交换

  •  0
  • meguli  · 技术社区  · 6 年前

    en.cppreference.com 我们看到了 std::stable_partition 如果允许我们使用额外的内存,就执行O(N)交换。我能看到这个。每次我们在谓词为假的范围内找到一个元素时,我们都会将其交换到另一个缓冲区中。最后,我们可以将这个额外的缓冲区复制到成功部分的末尾。[我也假设,在这种情况下, 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)交换。我想了想,我不知道我怎么能得到最坏的情况。

    我检查了llvm中的实现,但无法真正了解如何实现O(nlogn)交换。

    附:我的实现可能是错误的。我用几个输入来测试它,但就是这样。

    2 回复  |  直到 6 年前
        1
  •  3
  •   user58697    6 年前

    递归地思考。

    如果左半部分和右半部分都是稳定分区,如

    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
  •  1
  •   Dici gla3dr    6 年前

    我不知道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 .