From C++ documentation on inplace_merge, the complexity of the algorithm is "Linear in comparisons (N-1) if an internal buffer was used, NlogN otherwise (where N is the number elements in the range [first,last))". What do they mean by an internal buffer and what causes a complexity of O(N-1) vs. O(NlogN)?
问题:
回答1:
An internal buffer is simply a buffer allocated by the library of sufficient size to hold the output of the merge while the merge is happening (it's copied back to the original range after the merge is complete). If this extra space is used, the merge can be done in linear time. If it can't or doesn't use a separate buffer to store the output then the operation degrades to a general purpose sort with runtime O(n log n)
.
回答2:
To expand on the other answer(s):
At least in libstdc++ and libc++, the "internal buffer" is provided by calling
std::get_temporary_buffer
, an obscure yet standard routine in the STL. This routine has been deprecated in C++17, basically because it's confusing and kind of dumb. See this question for details, or read Stephan T. Lavavej's original deprecation proposal.In libstdc++,
get_temporary_buffer
is implemented as a call tooperator new
. (Source)In libc++, the
get_temporary_buffer
is implemented as a call tooperator new
. (Source)I don't know whether
inplace_merge
usesget_temporary_buffer
in MSVC's standard library, but I would bet money that it does.In MSVC, it has been reported that
get_temporary_buffer
is implemented as a call tooperator new
.
You can write a program to observe this call to operator new
firsthand by overriding operator new
in the global namespace:
#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: The "internal buffer" is allocated on the heap by calling operator new
. Implementations are not required to call operator new
, but in practice they all do. Stay far, far away from inplace_merge
if you value your heap.