源代码:
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
int a[N],b[N];
long long merge_sort(int a[],int l,int r)
{
if(l >= r) return 0;
int mid = (l + r) / 2;
//左右子数组逆序对之和
long long ans = merge_sort(a,l,mid) + merge_sort(a,mid + 1,r);
//归并
int i = l,j = mid + 1,k = 0;
while(i <= mid && j <= r)
{
if(a[i] <= a[j]) b[k ++] = a[i ++];
else
{
b[k ++] = a[j ++];
ans += mid - i + 1; //第三种,i再左,j在右
}
}
while(i <= mid) b[k ++] = a[i ++];
while(j <= r) b[k ++] = a[j ++];
//物归原主,归并排序的模板不要变
for(i = l,k = 0;i <= r;i ++,k ++) a[i] = b[k];
return ans; //注意,返回值为long long类型
}
int main()
{
cin>>n;
for(int i = 0;i < n;i ++) cin>>a[i];
cout << merge_sort(a,0,n - 1);
return 0;
}
运行结果:
心得:
首先解题方法是基于归并排序,再归并排序的模板上进行修改。
在归并排序中,我们将数组分成左右两个子数组,现在我们来查找逆序对,我们同样使用 i 和 j 作为指针指向一对数组元素,现在分成三种情况讨论:
第一种:i 和 j 都在左数组中;
第二种:i 和 j 都在有数组中;
第三种:i 在做数组,j 在有数组中。
我们定义递归函数merge_sort的返回值为在相应的数组段中存在的逆序对,对于前两种情况,我们将左子数组的返回值和右子数组的返回值相加赋给merge_sort返回值ans(对照源代码理解),接下来判断第三种情况。在第一个while循环中,我们判断a【i】和a【j】的大小,只有当a【j】大于a【i】时,才存在逆序对,那么有多少个呢?答案是 mid - i + 1。因为左子数组下标 i 之后的元素都大于a【j】,i 之前的元素都小于a【j】。
这道题我其实有个地方不明白:
一是这题并不需要输出排序后的数组,为什么还要将临时数组b中的元素还原给a数组,为什么不能删去merge_sort函数return上面的三条代码。