题目:
在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对总数。
例如数组{7,5,6,4|中,存在5个逆序对:(7,6),(7,5),(7,4),(6,4),(5,4)。
最直接的做法:顺序扫描整个数组,每扫描到一个数字,逐个比较该数字和它后面数字的大小。如果后面数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有n个数字,由于每个数字都要和O(n)个数字比较,因此这个算法事件复杂度为O(n^2)。
考虑比较相邻数字:以{7,5,6,4}为例
依次把数组分解成长度为n/2,n/4...1的子数组。
在第一对长度为1的子数组{7}、{5}中7>5,因此(7,5)组成一个逆序对。
在第二对长度为1的子数组{6}、{4}中6>4,因此(6,4)组成一个逆序对。
由于已经统计了两对子数组内部的逆序对,因此需要把这两对子数组排序,以免重复统计。
用两个指针分别指向两个子数组的末尾,并每次比较两个指针指向的数字。
如果P1>P2,那么构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的数目。
如果P1<=P2,那么不构成逆序对。
每一次比较的时候,都会把较大的数字从后往前复制到一个辅助数组中,确保辅助数组中的数字是递增排序的。
在把较大的数字复制到辅助数组后,把相应指针向前移动一位,接下来进行下一步比较。
int InversePairsCore(int* data,int* copy,int start,int end)
{
if(start==end)
{
copy[start]=data[start];
return 0;
}
int length=(end-start)/2;
int left=InversePairsCore(copy,data,start,start+length);
int right=InversePairsCore(copy,data,start+length+1,end);
//i初始化为前半段最后一个数字的下标
int i=start+length;
//j初始化为后半段最后一个数字的下标
int j=end;
int indexCopy=end;
int count=0;
while(i>=start && j>=start+length+1)
{
if(data[i]>data[j])
{
copy[indexCopy--]=data[i--];
count+=j-start-length;
}
else
{
copy[indexCopy--]=data[j--];
}
}
for(;i>=start;--i)
copy[indexCopy--]=data[i];
for(;j>=start+length+1;--j)
copy[indexCopy--]=data[j];
return left+right+count;
}
int InversePairs(int* data,int length)
{
if(data==NULL ||length<0)
return 0;
int* copy=new int[length];
for(int i=0;i<length;i++)
copy[i]=data[i];
int count=InversePairsCore(data,copy,0,length-1);
delete[] copy;
return count;
}