归并排序中统计逆序对的关键是在merge时,当arr[i] > arr[j],累加mid - i + 1个逆序对;需用临时数组合并、递归累加左右子区间结果,并注意下标边界与long long防溢出。

归并排序过程中怎么顺便数逆序对
归并排序天然适合统计逆序对,因为每次 merge 时,左半段和右半段各自已有序,一旦发现 left[i] > right[j],说明 left[i] 及其后面所有元素(共 mid - i + 1 个)都大于 right[j],这正是逆序对的批量来源。
关键不是“额外加一层循环去检查”,而是把计数逻辑嵌进 merge 的比较分支里。
常见错误是只在 left[i] > right[j] 时加 1,漏掉整个左段剩余部分;或者把计数放在错误位置(比如 merge 前递归调用后),导致重复或遗漏。
- 必须在
right[j]被写入临时数组前,累加mid - i + 1 - 递归调用要分别获取左右子区间的逆序对数量,再加本次
merge新产生的 - 原数组不能直接修改后再计数——得用临时数组合并,否则破坏有序性影响后续判断
C++ 实现中要注意的参数和边界
标准归并排序的 mergeSort(arr, l, r) 签名没问题,但计数函数最好返回 long long,避免大量逆序对时溢出(比如 n = 1e5 时最多约 5e9 对)。
merge 函数需接收原数组、左右边界、以及一个临时缓冲区(推荐传入 vector<int>& temp</int> 避免反复构造)。
容易错的下标:假设当前处理 [l, r],中点是 mid = l + (r - l) / 2,那么左段是 [l, mid],右段是 [mid + 1, r]。计数时,当 arr[i] > arr[j],左段剩余个数是 mid - i + 1,不是 r - i + 1。
示例片段:
long long merge(vector<int>& arr, vector<int>& temp, int l, int mid, int r) {
int i = l, j = mid + 1, k = l;
long long inv_count = 0;
<pre class="brush:php;toolbar:false;">while (i <= mid && j <= r) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
inv_count += (mid - i + 1); // 关键:左段从 i 到 mid 都构成逆序
}
}
while (i <= mid) temp[k++] = arr[i++];
while (j <= r) temp[k++] = arr[j++];
for (i = l; i <= r; ++i) arr[i] = temp[i];
return inv_count;
}
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
为什么不能用 std::inplace_merge 直接替代
std::inplace_merge 虽然功能等价,但它不暴露合并过程,无法插入计数逻辑。你没法知道哪一步触发了“左大于右”的跨段比较。
即使自己封装一个带回调的 merge,标准库也不提供访问内部比较次数或位置的接口。强行用它只会逼你退回到暴力 O(n²) 检查,失去归并的 O(n log n) 优势。
另一个现实约束:std::inplace_merge 要求输入迭代器支持随机访问,且实际实现可能用缓冲区或分治优化,行为不可控——计数必须基于你完全掌控的合并流程。
- 别试图 hook 或重载比较函数来计数:
std::inplace_merge内部比较的是元素值,不是索引,无法区分“哪个左元素导致了这次比较” - 如果坚持用 STL,只能自己写
merge,然后调用std::copy和std::move做数据搬运,但不如手写清晰
输入含重复元素时逆序对怎么定义
逆序对通常定义为满足 i arr[j] 的索引对。注意是严格大于(>),不是大于等于(>=)。所以 [2, 2, 1] 中只有两对:(0,2) 和 (1,2),不是三对。
这个定义直接影响 merge 分支判断:必须用 if (arr[i] 让相等时先取左边,保证相等元素不触发计数;若写成 <code>,则相等时会进 else 分支,错误计数。
调试时可拿小样例验证:[2, 1, 1] 应返回 2((0,1) 和 (0,2)),若返回 3 就说明把相等情况误判为逆序了。
边界容易忽略的一点:当左段耗尽(i > mid)或右段耗尽(j > r)时,不再产生新逆序对,这部分不用处理计数逻辑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










