逆序数概念:
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
即,在数组中,若0=<i<j<=n-1,有a[i]>a[j],就说明这两个数是逆序对,一个数组中逆序对数量就是逆序数。
代码实现思路:
第一种思路:用两个for循环解决,用第一个循环i从数组开头开始,另一个循环j从i+1开始,即此时满足了,i<j,比较a[i]和a[j]的大小,若a[i]>a[j],那就逆序数+1.
这种方法的是时间复杂度是O(n^2),n为数组的长度。若当n=100000的时候,就会超时。
所以这种方法的缺陷就是时间复杂度过大,下面提出另一种思路。
第二种思路:参考归并排序的思想(具体归并排序可参考我另一篇博客: 归并排序原理讲解及C++实现)
1)将数组分成两部分,分别求出左半边逆序数和右半边的逆序数
2)再计算有多少逆序是由左半边取一个数和右半边取一个数构成的
其中,第二小步的关键:左半边和右半边要排好序(因为是找前面比后面大的,所以一般从大到小排)。
这种方法的时间复杂度是O(nlogn),比第一种方法的时间复杂度要低。
综上所述,所以一般使用第二种,归并排序的思想来做,防止超时。
逆序数算法实现代码(C++):
#include<iostream>
#include<cstdio>
using namespace std;
int a[100010];
int b[100010];
void Merge(int a[],int s,int m,int e,int b[])
{
int pb = 0;
int p1 = s,p2 = m+1;
while(p1<=m && p2<=e)
{
if(a[p1] > a[p2])
b[pb++] = a[p1++];
else
b[pb++] = a[p2++];
}
while(p1<=m)
b[pb++] = a[p1++];
while(p2<=e)
b[pb++] = a[p2++];
for(int i = 0;i < e-s+1;i++)
a[s+i] = b[i];
}
long long Count(int a[],int s,int m,int e)
{
long long res = 0;
int p1 = s;
int p2 = m+1;
while(p1<=m && p2<=e)
{
if(a[p1] > a[p2])
{
res += (e-p2+1);
p1++;
}
else
p2++;
}
return res;
}
long long MergeSort_Res(int a[],int s,int e,int b[])
{
long long res = 0;
if(s < e)
{
int m = s + (e-s)/2;
res += MergeSort_Res(a,s,m,b);
res += MergeSort_Res(a,m+1,e,b);
res += Count(a,s,m,e);
Merge(a,s,m,e,b);
}
return res;
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 0;i < n;i++)
scanf("%d",&a[i]);
printf("%lld\n",MergeSort_Res(a,0,n-1,b));
return 0;
}