#include <iostream>
#include <vector>
using namespace std;
// 归并排序并计算逆序对数量
long long mergeSort(vector<int>& nums, int left, int right) {
if (left >= right) return 0;
int mid = left + (right - left) / 2;
// 分治递归
long long count = mergeSort(nums, left, mid) + mergeSort(nums, mid + 1, right);
// 归并合并
vector<int> temp(right - left + 1);
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) {
if (nums[i] <= nums[j])
temp[k++] = nums[i++];
else {
temp[k++] = nums[j++];
count += mid - i + 1; // 统计逆序对数量
}
}
while (i <= mid)
temp[k++] = nums[i++];
while (j <= right)
temp[k++] = nums[j++];
for (int p = 0; p < k; ++p)
nums[left + p] = temp[p];
return count;
}
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; ++i)
cin >> nums[i];
// 调用归并排序函数并输出逆序对数量
cout << mergeSort(nums, 0, n - 1) << endl;
return 0;
}
- 首先定义了一个
mergeSort
函数,用于进行归并排序并计算逆序对的数量。 - 在
mergeSort
函数中,首先判断如果左指针大于等于右指针,则表示当前序列只有一个元素或者为空,不需要排序,直接返回逆序对数量为0。 - 计算中间位置的下标
mid
,然后进行分治递归:
- 递归调用
mergeSort
函数对左半部分进行排序,并将返回的逆序对数量累加到count
中。 - 递归调用
mergeSort
函数对右半部分进行排序,并将返回的逆序对数量累加到count
中。 - 创建一个临时数组
temp
,大小为右指针减去左指针加1。 - 使用三个指针
i
、j
、k
分别指向左半部分、右半部分和临时数组的位置,进行归并操作: - 当
nums[i]
小于等于nums[j]
时,将nums[i]
放入临时数组,并将指针i
和k
向后移动。 - 当
nums[i]
大于nums[j]
时,将nums[j]
放入临时数组,并将指针j
和k
向后移动。此时,右半部分的元素都小于nums[i]
,因此可以统计逆序对数量为mid - i + 1
,将该值累加到count
中。(核心思想) - 将左半部分或右半部分剩余的元素依次放入临时数组。
- 将临时数组中的元素复制回原始数组
nums
的相应位置。 - 返回逆序对的数量
count
。 - 在
main
函数中,首先读取数列的长度n
,然后创建一个大小为n
的整数数组nums
,并依次读入数列的元素。 - 调用
mergeSort
函数对数列进行排序并输出逆序对的数量。
Q:为什么可以统计统计逆序对数量为 mid - i + 1?
A:在归并排序的过程中,当合并左右两个有序数组时,如果左数组中的某个元素 nums[i]
大于右数组中的某个元素 nums[j]
,则说明左数组中 i
及其之后的元素都大于 nums[j]
。因为左右数组都是有序的,所以可以推断左数组中 i
及其之后的元素与 nums[j]
构成逆序对。
在归并排序的合并过程中,当将 nums[j]
放入临时数组时,此时 i
到 mid
的元素都是满足逆序对条件的。因此,逆序对的数量就是 mid - i + 1
。
具体推导如下:
- 在合并过程中,当
nums[i] > nums[j]
时,说明nums[i]
及其之后的元素都大于nums[j]
。 - 假设
i
到mid
的元素个数为x
,则逆序对的数量为x
。 - 因此,逆序对的数量为
mid - i + 1
。