std::inplace_merge用于原地合并相邻有序区间,要求[first,middle)和[middle,last)各自有序且连续,支持随机访问迭代器,底层可能分配缓冲区,内存不足时降级为o(n log n)。

std::inplace_merge 用来合并相邻的两个有序子区间
它不创建新容器,而是在原容器内完成归并,前提是两段数据必须紧挨着、且各自有序。比如 vector 的前半段升序、后半段也升序,你想让整个 vector 变成升序,这时就轮到 std::inplace_merge 出场。
常见错误是以为它能合并任意两段不连续的数据——不行,必须是同一块内存里“头挨着尾”的两个有序区间。传错迭代器位置,结果就是未定义行为,可能 crash 或输出乱序。
- 必须保证
[first, middle)和[middle, last)各自有序(升序默认,也可传自定义比较) - 不能用于
list以外的链表式容器——等等,list其实有自己专属的merge()成员函数,std::inplace_merge主要针对vector、deque这类支持随机访问的序列 - 底层通常用缓冲区做归并;若内存紧张,会退化为 O(n log n) 比较次数,但仍是 in-place 的
怎么调用 std::inplace_merge?三个参数缺一不可
最简形式是 std::inplace_merge(first, middle, last),三者都是迭代器。关键在于:你得自己把“两段有序区间的分界点”算清楚,middle 就是第二段的起点,也是第一段的终点。
比如一个 vector<int> v = {1,3,5,2,4,6}</int>,前 3 个和后 3 个各自有序,想合并成全有序,就得传 v.begin()、v.begin() + 3、v.end()。
立即学习“C++免费学习笔记(深入)”;
- 如果误把
middle设成v.begin() + 2,那第一段变成{1,3},第二段变成{5,2,4,6}——后者根本无序,结果不可预测 - 支持自定义比较,比如降序合并:
std::inplace_merge(first, middle, last, std::greater<int>())</int> - 注意:
first和last必须来自同一个容器,且first ≤ middle ≤ last,否则 UB
为什么不用 merge + assign,而选 inplace_merge?
因为省空间。如果你用 std::merge,得额外开一块大小为 std::distance(first, last) 的内存存结果,再拷回去;而 std::inplace_merge 尽量复用原空间,对内存受限场景更友好。
但它不是零代价:标准不保证具体算法,libstdc++ 和 libc++ 实现不同。libstdc++ 优先尝试用临时缓冲区加速到 O(n),失败则回退;libc++ 更倾向稳定 O(n log n) 但内存占用更低。所以性能表现要看实际数据规模和可用内存。
- 小数组(比如几十个元素):差别几乎感知不到
- 大数组 + 内存充足:带缓冲的
inplace_merge通常比手写 merge+copy 快 - 大数组 + 内存吃紧:可能触发多次分配失败路径,反而慢,此时手动分块或换算法更稳
容易被忽略的兼容性细节
它要求迭代器至少是双向迭代器(bidirectional_iterator),所以不能用于 forward_list 或输入流迭代器。另外,C++17 起才支持执行策略(如 std::execution::par),但主流编译器至今没几个真正实现并行版——别指望加个策略就自动变快。
- 在
deque上用没问题,但要注意:deque的“连续性”是逻辑上的,inplace_merge内部仍按块搬运,性能略逊于vector - 对
string也能用,但字符类型得支持比较操作;窄字符串(std::string)没问题,宽字符串(std::wstring)也行,只要char_traits定义了compare - 移动语义生效的前提是元素可移动;如果元素类型没有移动构造函数,就会退化为拷贝,这时候“in-place”只是地址不变,实际开销未必小
真正 tricky 的地方在于:它既不是纯内存操作,也不完全避开分配——那个内部缓冲区申请失败时的行为,文档只说“抛异常或降级”,具体怎么降级,得看库实现。线上服务里用它,最好先压测下边界情况。










