原始题目
下面归并排序代码横线处应该填什么?
int mergeSort(int a[], int temp[], int left, int right) {
if (left >= right) return 0;
int mid = left + (right - left) / 2;
int count = mergeSort(a, temp, left, mid) + mergeSort(a, temp, mid + 1, right);
int i = left, j = mid + 1, k = left;
while (i <= mid && j <= right) {
if (a[i] <= a[j]) temp[k++] = a[i++];
else {
temp[k++] = a[j++];
___;
}
}
while (i <= mid) temp[k++] = a[i++];
while (j <= right) temp[k++] = a[j++];
for (int i = left; i <= right; i++) a[i] = temp[i];
return count;
}
- A.
count++ - B.
count += mid - i + 1 - C.
count += j - left - D.
count += right - j + 1
答案:B(
count += mid - i + 1)
完整写法:
int mergeSort(int a[], int temp[], int left, int right) {
if (left >= right) return 0;
int mid = left + (right - left) / 2;
int count = mergeSort(a, temp, left, mid) + mergeSort(a, temp, mid + 1, right);
int i = left, j = mid + 1, k = left;
while (i <= mid && j <= right) {
if (a[i] <= a[j]) temp[k++] = a[i++];
else {
temp[k++] = a[j++];
count += mid - i + 1; // 逆序对累计
}
}
while (i <= mid) temp[k++] = a[i++];
while (j <= right) temp[k++] = a[j++];
for (int i = left; i <= right; i++) a[i] = temp[i];
return count;
}
图解:合并阶段如何数逆序对
原理
左边子数组 [left, mid]、右边 [mid+1, right] 都已经在各自区间有序。当 a[i] > a[j](走 else)时:
- 因为左边有序,
a[i]是左边未处理部分里最小的 - 它都比
a[j]大,那a[i]后面的a[i+1], … , a[mid]自然也都比a[j]大 - 所以
a[j]和左边剩余所有元素都构成逆序对,数量是mid - i + 1
这就是为什么不能写 count++——它只数了「这一对」,漏掉了左边整批。
逐项排除
| 选项 | 判断 | 错在哪 |
|---|---|---|
A. count++ |
❌ | 只数了 (i, j) 这一对,漏掉左边 i+1…mid 那一批,结果严重偏小 |
B. count += mid - i + 1 |
✅ | 左边剩余全部元素都与 a[j] 构成逆序对 |
C. count += j - left |
❌ | 用右边下标算逆序对数,量纲完全不对 |
D. count += right - j + 1 |
❌ | 这是右边剩余元素个数,与当前比较无关 |
复杂度
| 操作 | 时间 | 空间 |
|---|---|---|
| 暴力枚举所有对 | \(O(n^2)\) | \(O(1)\) |
| 归并排序法(本题) | \(O(n \log n)\) | \(O(n)\) |
\(n\) 较大(如 \(10^5\))时,暴力会超时,必须用归并或树状数组/线段树。
易错点
<=的位置:a[i] <= a[j]时归并,等于不算逆序对,mid - i + 1仍正确。- 重复元素:用
<=把等于归入“不逆序”,避免重复计数(某些题要求逆序对严格>才算)。 - int 溢出:逆序对总数最大约 \(n(n-1)/2\),
n = 10^5时约为 \(5 \times 10^9\),必须开long long,否则中间结果溢出。 - 稳定计数:归并排序本身稳定,逆序对计数结果与具体相等元素顺序无关(前提是严格
>才算)。
考点总结(CSP-J/S 初赛、程序填空)
- 出现“逆序对”+“归并排序”组合 →
else分支填count += mid - i + 1。 - 记忆口诀:左边还有几个没处理,就新增几个逆序对。
- 总逆序对 = 左半内部 + 右半内部 + 合并时跨左右的。
count在递归里一路累加返回。 n大时记得long long,否则爆 int。
来源
本文解析参考自:https://123.hnai.net/nuclq
思考题
如果要求“逆序对”改为“顺序对”(即 a[i] < a[j],i < j 的对数),应该怎么改这行代码?
答案
顺序对等于“总对数 − 逆序对 − 相等对数”。更直接的改法:把条件改成 a[i] < a[j] 时统计,else 里对应 a[j] <= a[i] 的情况累计。常用写法是用总对数 \(n(n-1)/2\) 减去逆序对与相等对的数量,避免改核心逻辑。



![BFS 为什么能求最短路?—— dist[v] = dist[u] + 1 与分层思想](https://gesp-img.oss-accelerate.aliyuncs.com/uploads/202609/13/199d2ea51279b83f.png)