C++中std::merge与std::inplace_merge的区别_C++归并排序操作【操作】

1次阅读

std::merge用于合并两个独立有序区间到新容器,需额外空间且输出区域须足够大;std::inplace_merge则原地合并单个容器中两段相邻有序子区间,要求随机访问且不重叠。

C++中std::merge与std::inplace_merge的区别_C++归并排序操作【操作】

std::merge 是非原地合并,需要额外空间

它把两个已排序的区间合并成一个新序列,结果写入第三个容器或内存区域。最常见用法是合并两个 std::vector 到第三个 std::vector 中。

容易踩的坑:

  • std::merge 不修改输入区间,但要求输出迭代器指向的空间足够大(至少 std::distance(first1, last1) + std::distance(first2, last2)
  • 如果输出目标和某个输入区间重叠(比如把结果写回其中一个输入容器),行为未定义
  • 必须确保两个输入区间各自有序,否则结果不可预测

示例:

std::vector a = {1, 3, 5}; std::vector b = {2, 4, 6}; std::vector out(6); std::merge(a.begin(), a.end(), b.begin(), b.end(), out.begin()); // out == {1,2,3,4,5,6}

std::inplace_merge 是原地合并,但要求输入连续且可随机访问

它把一个容器中**两段相邻的有序子区间**合并成一段有序区间,不分配新内存,但内部可能做元素移动或旋转操作。

立即学习C++免费学习笔记(深入)”;

关键限制:

  • 输入必须是单个连续容器(如 std::vectorstd::deque),不能是两个分离容器
  • 要指定一个分割点(middle 迭代器),使得 [first, middle)[middle, last) 各自有序
  • 时间复杂度平均 O(n),但最坏可能达 O(n log n);空间复杂度 O(log n)(递归或缓冲区)

示例:

std::vector v = {1, 3, 5, 2, 4, 6}; // 前3个有序,后3个有序 auto mid = v.begin() + 3; std::inplace_merge(v.begin(), mid, v.end()); // v == {1,2,3,4,5,6}

归并排序实现中该选哪个?

标准库没提供直接的归并排序函数,但你可以用这两个函数组合实现。实际选择取决于数据结构和内存约束:

  • 若你手写分治式归并排序(如对 std::vector 排序),通常用 std::inplace_merge 来合并左右子段——前提是它们在内存中相邻且已排好序
  • 若你在处理两个独立的已排序序列(比如日志文件按时间合并),std::merge 更自然,也更安全
  • std::inplace_mergestd::list 有特化版本(O(n) 时间、O(1) 额外空间),此时它比 std::merge 更省空间,但注意 std::list::merge() 成员函数才是为链表设计的首选

常见错误:误用 inplace_merge 导致未定义行为

最典型的错误是传入非相邻或非有序的区间,或者在不支持随机访问的容器(如 std::forward_list)上调用它。

例如:

  • std::forward_list 调用 std::inplace_merge —— 编译失败(无匹配重载)
  • v.begin()+1 当作 middle,但 [v.begin(), v.begin()+1)[v.begin()+1, v.end()) 实际并不有序 —— 结果混乱,无报错提示
  • std::inplace_merge 处理两个 std::Array 拼接后的视图(非真正连续内存)—— 行为未定义

调试时建议先用 std::is_sorted 检查左右两段是否真有序,再调用 std::inplace_merge

真正难的不是调用哪个函数,而是保证传给它们的区间满足前提条件:有序性、连续性、可访问性。这些约束不会在编译时报错,但会在运行时悄悄破坏数据。

text=ZqhQzanResources