c++oding="utf-8" ?>
std::inplace_merge要求两段相邻有序区间,参数为first、middle、last,且需满足first≤middle≤last;传入非法middle或非相邻/无序区间将导致未定义行为;合并后迭代器可能失效,建议重新获取;性能与内存开销取决于实现,未必是O(n)时间与O(1)空间。

std::inplace_merge合并两个相邻有序子序列的正确姿势
它只接受三个迭代器:前半段起点、前半段终点(也是后半段起点)、后半段终点。不能跨区域合并,也不能合并不相邻的两段。
常见错误是传错中间点,比如把整个容器的 begin() 和 end() 直接塞进去,结果程序崩溃或行为未定义——std::inplace_merge 要求输入必须是「两段紧挨着的已排序区间」。
- 确保
[first, middle)和[middle, last)各自升序(或同为降序,需配合自定义比较) - 中间点
middle必须是合法迭代器,且满足first ≤ middle ≤ last - 若用
std::greater排序,合并时也得传同样的比较器,否则结果错乱()
为什么 merge 后原容器迭代器可能失效?
std::inplace_merge 内部可能做就地旋转、归并或缓冲区拷贝,标准不保证迭代器稳定。尤其在小容器上用 libstdc++ 时,它倾向用 std::rotate 实现,而 rotate 不会让迭代器失效;但换成 libc++ 或数据量大时,可能分配临时缓冲区并重排内存,导致原有迭代器悬空。
- 合并前持有的
vector::iterator、list::iterator在调用后不要直接解引用 - 如需后续操作,建议合并完重新获取迭代器,例如
v.begin() -
std::list上的inplace_merge是稳定操作,节点不移动,迭代器保持有效
性能和内存开销的实际表现
它不是纯 O(n) 时间 + O(1) 空间。标准只要求“尽可能就地”,实际取决于实现:
- libstdc++:当可用内存足够,会分配 O(n) 临时缓冲区,做到稳定 O(n) 时间;否则退化为多次
rotate,最坏 O(n log n) - libc++:默认使用缓冲区,但可通过
_VSTD::__inplace_merge底层接口控制策略(不推荐用户直接调) - 对
std::deque,因内存不连续,基本必然触发缓冲区分配
如果你明确不能分配额外内存,又需要合并,得自己写基于旋转的版本,或者改用 std::merge 到新容器再 swap。
一个容易被忽略的边界:空子序列处理
当 first == middle 或 middle == last 时,std::inplace_merge 是合法且无操作的——但它不会帮你检测逻辑错误。比如你误把分割点算错成越界位置,导致 middle > last,行为就是未定义。
- 调试时可加断言:
assert(first - 对空容器调用没问题:
v.empty() ? inplace_merge(v.begin(), v.begin(), v.end()) : ... - 注意
std::vector是特化,其迭代器不满足 RandomAccessIterator 要求,不能用于inplace_merge
真正麻烦的是搞混了“已排序”这个前提——它不校验,也不排序,只合并。输进去乱序,出来还是乱序,只是结构更难看一点。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











