10.给定一个inversion的定义如下:对于一个整型数组A,以及任意的下标i,j,其中i<j,如果A[i]>A[j],我们称它为一个inversion.请设计一种算法用来计算给定数组A中的inversion的个数。
答: 改进归并排序算法。先递归分治,然后合并两段已经由小到大排序的序列为一段降序的序列。排序过程就是消除逆序对的过程。现假设两段有序序列分别是[beg,mid]和[mid+1,end],在归并过程中(i,j分别为两段序列的下标),如果a[i]<a[j],则不会产生逆序对;但当a[i]>a[j]时,就出现逆序对了,出现了多少?既然[beg,mid]是有序的,那么[i-mid]序列就都能与a[j]构成逆序对,故:mid-i+1。逆序对数=两段有序序列排序前的逆序对数+两段有序序列合并过程中发现的逆序对数。一次合并过程需要得到合并中发现的逆序对数,同时在过程结束时得到一个已经排好序的序列,用于下一次合并。
自顶向下实现:
//在原数组上直接升序排序,调用merge时,a[start~mid],a[(mid+1)~end]均已由小到大排好序,调用结束时,a[start~end]也均已降序排好
#define N 10
int merge(int *a, int start, int mid, int end)
{
int tmp[N];
int pfisrt = start;
int psecond = end;
int ptmp = 0;
int num_inversion = 0;
int i;
while(pfirst <= mid && psecond <= end)
{
if(a[pfirst] <= a[psecond])
{
tmp[ptmp] = a[pfirst];
ptmp++;
pfirst++;
}else if(a[pfirst] > a[psecond])
{
num_inversion += (end - pfirst + 1);
tmp[ptmp] = a[psecond];
psecond++;
ptmp++;
}
}
while(pfirst <= mid)
{
tmp[ptmp] = a[pfirst];
ptmp++;
pfirst++;
}
whilt(psecond <= end)
{
tmp[ptmp] = a[psecond];
ptmp++;
psecond++;
}
for(i = start; i <= end; i++)
{
a[i] = tmp[i - start];
}
return num_inversion;
}
int count_inversion(int *a, int start, int end)
{
if(start >= end)
{
return 0;
}
int mid = start + (end - start)/2;
return count_inversion(a,start,mid)+count_inversion(a,mid+1,end)+merge(a,start,mid,end);
}