逆序数:序列中逆序对的个数。
例如:9 1 0 5 4
由于要把它排列为上升序列,上升序列的有序就是后面的元素比前面的元素大,而对于序列9 1 0 5 4
9后面却有4个比9小的元素,因此9的逆序数为4
1后面只有1个比1小的元素0,因此1的逆序数为1
0后面不存在比他小的元素,因此0的逆序数为0
5后面存在1个比他小的元素4, 因此5的逆序数为1
4是序列的最后元素,逆序数为0
因此序列9 1 0 5 4的逆序数 t=4+1+0+1+0 = 6。
/*
归并排序是用分治思想,分治模式在每一层递归上有三个步骤:
分解:将n个元素分成个含n/2个元素的子序列。
解决:用合并排序法对两个子序列递归的排序。
合并:合并两个已排序的子序列已得到排序结果。
*/
将数组A[1...size],划分为A[1...mid] 和 A[mid+1...size].
那么逆序对数的个数为 f(1, size) = f(1, mid) + f(mid+1, size) + s(1, mid, size),这里s(1, mid, size)代表左值在[1---mid]中,右值在[mid+1, size]中的逆序对数。由于两个子序列本身都已经排序,所以查找起来非常方便。
void Merge(int Left,int Middle,int Right)
{
int n1=Middle-Left+1;
int n2=Right-Middle;
for(int i=1;i<=n1;i++)
L[i]=A[Left+i-1];
for(int i=1;i<=n2;i++)
R[i]=A[Middle+i];
L[n1+1]=M; // M是比较大的数。
R[n2+1]=M;
int i=1;int j=1;
for(int k=Left;k<=Right;k++)
{
if(L[i]<=R[j])
A[k]=L[i++];
else
{
A[k]=R[j++];
cunt+=n1-i+1; //cunt为全局变量。最后cunt即为逆序对的个数。
}
}
}
递归的合并排序,如果子数组中至多有一个元素,当然是已排好,否则分解。
void Merge_sort(int Left,int Right)
{
int Middle;
if(Left<Right)
{
Middle=(Left+Right)/2;
Merge_sort(Left,Middle); // 二分分解左部分
Merge_sort(Middle+1,Right); // 二分分解有部分
Merge(Left,Middle,Right); //合并两部分
}
}
设数组为A,关键是在合并的时候,用数组L 和 R 表示左右两个子数组,因为逆序对的个数f(A) = f(L) + f(R) + s(L,R);
其中f(L) 和 f(R) 分别表示L 内部 和R内部的逆序对个数,s(L.R)表示大数在L,小数在R的逆序对。
因为L和R是已经排好序的,故其实只需求s(L,R).这个可以在合并L和R依次进行比较的时候算出。
if(L[i]<=R[j])
a[k] = L[i++];
else //如果L最上面的数大于R的,那么L[i]及后面的数可以和R[j]构成n1-i+1个逆序对
a[k] = R[j++];
count +=(n1 -i + 1); //累加
}
}
归并排序的代码:
int n1 = q-p+1,n2 = r-q;
int i,j,k;
for(i = 1;i <= n1; i++)
L[i] = a[p+i-1];
for(j = 1;j <= n2; j++)
R[j] = a[q+j];
L[n1+1] = INF;
R[n2+1] = INF;
i = 1;j = 1;
for(k = p;k <= r;k ++){
if(L[i]<=R[j])
a[k] = L[i++];
else{
a[k] = R[j++];
count +=(n1 -i + 1);
}
}
}
void merge_sort(int p,int r){
if(p<r){
int q = (p+r)/2;
merge_sort(p,q);
merge_sort(q+1,r);
merge(p,q,r);
}
}