先是逆序对的定义:一个n个互异元素的数组a,求满足i<j时a[i]>a[j]条件的数对个数。
数据输入:n(元素个数),a数组
数据输出:逆序对个数
算法分析:这个题目十分的经典,是归并排序的一个完美应用,分治是其主要思想,具体可以概括假设f(i,j)为i到j号元素中的逆序对个数,取一个分割点k,假设s(i,j,k)表示以k为分割点,第一个元素在i到k中,第二个元素在k+1到j中形成的逆序对数。那么我们就得到一个递归式:f(i,j)=f(i,k)+f(k+1,j)+s(i,j,k)。很自然的与归并排序联系到了一起,对于更小规模的f可以递归求解,如果对于a数组的i到k以及k+1到j两个部分元素均为有序的情况,那么对于当a[j]<a[i]的情况,必然有a[j]<a[i..k],即a[j]和i到k号元素都形成逆序对,此时只要将s(i,j,k)加上k-i+1就可以了(i到k之间的元素个数),这个过程很像归并排序的Merge的过程——比较当前i和j的状态并放入较小的。于是我们就得到了算法,和归并排序一起操作:外围设置一个计数器cnt,每次归并过程分为分割与合并两个部分,分割照常处理,在合并部分中的if (a[i]>a[j]) b[++l]=a[++j];中加入一个计数过程,即cnt+=k-i+1。这样当排序完成后,统计也就完成了。
算法复杂度分析:时间上因为是和归并排序一起处理,所以时间复杂度应该是O(nlogn),同时因为是归并排序,所以还附加了O(n)的空间复杂度。
#include<iostream>
using namespace std;
static int count=0;
void merge(int a[],int p,int q,int r)
{
int n1=q-p+1;
int n2=r-q;
int * l=new int[n1+1];
int * rl=new int[n2+1];
for(int i=0;i<n1;i++)
l[i]=a[p+i-1];
for(int j=0;j<n2;j++)
rl[j]=a[q+j];
l[n1]=65535;
rl[n2]=65535;
i=0;
j=0;
for(int k=p-1;k<r;k++)
{
if(l[i]<=rl[j])
{
a[k]=l[i];
i++;
}
else
{
a[k]=rl[j];
j++;
count+=(q-i+1-p);
}
}
}
void mergeSort(int a[],int p,int r)
{
if(p<r)
{
int q=(p+r)/2;
mergeSort(a,p,q);
mergeSort(a,q+1,r);
merge(a,p,q,r);
}
}
int main()
{
int a[7]={6,5,4,3,2,1,0};
mergeSort(a,1,7);
for(int i=0;i<7;i++)
cout<<a[i];
cout<<endl;
cout<<count<<endl;
return 0;
}