【问题标题】:inplace_merge: What causes a complexity of N*log(N) vs. N-1?inplace_merge:是什么导致 N*log(N) 与 N-1 的复杂性?
【发布时间】:2012-07-06 18:24:00
【问题描述】:

从关于 inplace_merge 的 C++ 文档中,该算法的复杂性是“如果使用内部缓冲区,则比较线性 (N-1),否则为 NlogN(其中 N 是范围 [first,last) 中的数字元素)” .内部缓冲区是什么意思,是什么导致了 O(N-1) 与 O(NlogN) 的复杂性?

【问题讨论】:

标签: c++ merge complexity-theory


【解决方案1】:

内部缓冲区只是由库分配的一个缓冲区,其大小足以在合并发生时保存合并的输出(合并完成后它被复制回原始范围)。如果使用了这个额外的空间,则可以在线性时间内完成合并。如果它不能或不使用单独的缓冲区来存储输出,则操作将降级为运行时O(n log n) 的通用排序。

【讨论】:

  • 那可能是什么通用排序?我想不出一个稳定的,O(n log n) 时间和 o(n) 空间。
  • 它不会降级为通用排序。相反,如果没有足够的内存,则将较大的块切成两半。然后使用较大块的中间元素搜索另一个块的 lower_bound 或 upper_bound,具体取决于较大块是第一个还是第二个。将 2 个块分成 4 个块后,旋转 4 个块中的第 2 个和第 3 个块(可能使用任何临时块)。现在问题被简化为 inplace_merging 第一和第二块以及第三和第四块必须更小并且可能可以使用任何更小的缓冲区。
【解决方案2】:

扩展其他答案:

  • 至少在 libstdc++ 和 libc++ 中,“内部缓冲区”是通过调用 std::get_temporary_buffer 提供的,这是 STL 中一个晦涩但标准的例程。该例程在 C++17 中已被弃用,主要是因为它令人困惑且有点愚蠢。详情请参阅this question,或阅读Stephan T. Lavavej's original deprecation proposal

  • 在 libstdc++ 中,get_temporary_buffer 被实现为对 operator new 的调用。 (Source)

  • 在 libc++ 中,get_temporary_buffer 被实现为对 operator new 的调用。 (Source)

  • 我不知道 inplace_merge 是否在 MSVC 的标准库中使用 get_temporary_buffer,但我敢打赌它确实如此。

  • 在 MSVC 中,it has been reportedget_temporary_buffer 被实现为对 operator new 的调用。

您可以通过覆盖全局命名空间中的operator new 来编写一个程序到observe this call to operator new firsthand

#include <algorithm>
#include <cstdio>
#include <cstdlib>

void *operator new(size_t nbytes, const std::nothrow_t&) noexcept
{
    puts("hello from operator new!");
    return malloc(nbytes);
}

int main()
{
    int a[32] = {
        1,1,1,2,3,4,4,5,5,5,5,6,6,8,9,9,
        1,1,2,3,3,3,4,4,5,5,7,8,9,9,9,9,
    };
    std::inplace_merge(&a[0], &a[16], &a[32]);  // "hello from operator new!"
    (void)a;
}

TL;DR:通过调用operator new 在堆上分配“内部缓冲区”。调用operator new 不需要实现,但实际上它们都可以。如果您重视自己的堆,请远离inplace_merge

【讨论】:

  • 关于当没有足够的空间用于 O(N) 时它如何降级到 O(N log N) 的任何想法,即,可以使用什么算法?
  • @superbrain:好吧,除非我遗漏了什么,否则std::inplace_merge(first, mid, last, lessthan) 可以一致地实现为std::stable_sort(first, last, lessthan)。我不知道那里是否可以做一些“更聪明”的事情,但是stable_sort 是 O(N log N),对吧?
  • 不,在这种情况下(空间不足),显然只能保证为O(N log(N)^2)
  • 啊,找到了。 inplace_merge 有两个指向实现的链接,我们可以在其中看到算法(我认为两者几乎相同,但 libstdc++ 更容易阅读)。
  • 不同的问题,但是:你说“如果你重视你的堆,请远离inplace_merge。我应该怎么做?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-24
  • 2011-07-09
  • 2021-11-11
相关资源
最近更新 更多