文章转载自http://www.cnblogs.com/BeWater/archive/2013/03/03/2941488.html
归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。
在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,
在前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。
因此,可以在归并排序中的合并过程中计算逆序数。
源代码(C语言)
#include<stdio.h> #include<stdlib.h> _int64 inversionCount=0; void merge_and_count(int *a,int p,int q,int r){//从零开始计数 int i,j,t; i=p; j=q+1; t=0; int *templist = (int *)malloc(sizeof(int) * (r-p+1)); while(i<=q && j<=r){ if(a[i]<a[j]){ templist[t++] = a[i]; i++; } else{ templist[t++] = a[j]; inversionCount+=(q+1-i); j++; } } while(i<=q){ templist[t++] = a[i++]; } while(j<=r){ templist[t++] = a[j++]; } t=0; for(int k=p;k<=r;k++) a[k] = templist[t++]; } void merge_sort(int *a,int p,int r){ if(p<r){ int q=(p+r-1)/2; merge_sort(a,p,q); merge_sort(a,q+1,r); merge_and_count(a,p,q,r); } }
Quick_sort算法来数逆序数(这个多说一些)
该算法主体与quick_sort相似,给定一个序列,先找到一个分割点splitter,然后quick_sort关于splitter节点两半部的交换过程之间查找一个相对逆序数,包括三部分:左半部的相对逆序数,右半部的相对逆序数,左右两边的相对逆序数。分成两半之后,依次递归,得出结果。以{2 7 8 3 6 9 4 5 10}为例,令splitter=(left+right)/2,分为两部分S- 和 S+:
左半部(从右往左splitter-->left)
如果data[i]>data[splitter],数出位于该点右边小于或等于data[splitter]的元素的个数,将该计数(如图加粗画底线部分计数)加入逆序数计数InversionCount,并把该元素加入S+,如果data[i]<data[splitter],操作与quick_sort相似。
2 | 7 | 8 | 3 | 6(splitter) | 9 | 4 | 5 | 10 |
例如data[i] = 8 > data[splitter] = 6 ; 黑体部分计数为2,因此遍历到8时InversionCount += 2.
右半部(从左往右splitter-->right)
如果data[i] < data[splitter],数出位于该点左边小于或等于data[splitter]的元素的个数,将该计数(如图加粗画底线部分计数)加入逆序数计数InversionCount,并把该元素加入S-,如果data[i] > data[splitter],操作与quick_sort相似。
2 | 7 | 8 | 3 | 6(splitter) | 9 | 4 | 5 | 10 |
例如,data[i] = 5 < data[splitter] = 6,黑体部分计数为2,因此遍历到5时InversionCount += 2.
交叉部分:
数出splitter左半部比data[splitter]大的元素个数lr,数出splitter右半部比data[splitter]小的元素的个数rl,然后InversionCount +=lr * rl。例子中InversionCount += 2 * 2.
2 | 7 | 8 | 3 | 6(splitter) | 9 | 4 | 5 | 10 |
程序代码(C语言)
#include<stdio.h>
#include<stdlib.h>
_int64 InversionCount=0;
void merge_count(int *data,int & splitter,int left,int right){
if(left > splitter || splitter > right)
return;
int pivot= data[splitter];
int *tempLL,*tempLR,*tempRL,*tempRR;
int ll=0,lr=0,rl=0,rr=0;
int i;
bool flag1=false,flag2=false;
//左边求相对逆序数,遍历顺序从splitter往左
if(left < splitter){
flag1 = true;
tempLL = (int *)malloc(sizeof(int) * (splitter - left));
tempLR = (int *)malloc(sizeof(int) * (splitter - left));
for(i=splitter - 1; i>=left; i--)
if(data[i] < pivot)
tempLL[ll++] = data[i]; //ll为left部分目前比splitter小但离splitter最近的点的个数
else{
tempLR[lr++] = data[i];
InversionCount += ll+1; //ll+1 为该点对于left部分比splitter小但处于该点右边的点的逆序数
}
}
//右边求相对逆序数,遍历顺序从splitter往右
if(right>splitter){
flag2 =true;
tempRL = (int *)malloc(sizeof(int) * (right - splitter));
tempRR = (int *)malloc(sizeof(int) * (right - splitter));
for(i=splitter+1; i<=right; i++)
if(data[i] > pivot)
tempRR[rr++] = data[i]; //rr为right部分目前比splitter大但离splitter最近的点的个数
else{
tempRL[rl++] = data[i];
InversionCount += rr+1; //rr+1为该点对于right部分比splitter大但处于该点左边的点的逆序数
}
}
//加上left和right之间的相对逆序数
InversionCount += lr * rl;
//将新的序列拷贝到原数组,注意拷贝的顺序
int k=left;
for(i=ll-1; i>=0; i--)
data[k++] = tempLL[i];
for(i=0; i<rl; i++)
data[k++] = tempRL[i];
splitter = k;
data[k++] = pivot;
for(i=lr-1; i>=0; i--)
data[k++] = tempLR[i];
for(i=0; i<rr; i++)
data[k++] = tempRR[i];
if(flag1)
delete tempLL,tempLR;
if(flag2)
delete tempRL,tempRR;
}
void quicksort_count(int *data,int left,int right){
if(left>=right)
return;
int splitter = left;
merge_count(data, splitter, left, right);
quicksort_count(data,left,splitter-1);
quicksort_count(data,splitter+1,right);
}