导言
第一次了解到逆序数是在高等代数课程上。当时想计算一个数列的逆序数直觉就是用两重循环O(n^2)暴力求解。现在渐渐对归并算法有了一定的认识,因此决定自己用C++代码小试牛刀。
逆序数简介
由自然数1,2…,n组成的不重复的每一种有确定次序的排列,称为一个n级排列(简称为排列);或者一般的,n个互不同元素排成一列称为“一个n级排列”。例如,1234和4312都是4级排列,而24315是一个5级排列。
在一个n级排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个“逆序”。
例子:
1,2,3,4 成为自然排列 逆序数为 0
3,2,4,1 一列数 逆序排列有(3,2) (3,1) (2,1) (4,1) 所以逆序数是4
代码实现
#include <cstdio>
#include <cstring> const int N = ; //测试数组的大小 int cnt; //全局变量 void mergeSort(int *a,int p,int q,int *T){ if(p+>=q) return; int m=p+(q-p)/; mergeSort(a,p,m,T); mergeSort(a,m,q,T); //merge for(int x=p,y=m,i=p;i<q;i++){ if(x<m&&y<q&&a[x]<a[y] || y>=q) T[i]=a[x++]; //往‘左边’加 else{ T[i] = a[y++]; cnt += (m-x); //此处为重点,每向加入右边部分一个数时,逆序数应增加左边尚未被加入T的元素个数 } } for(int i=p;i<q;i++) a[i] = T[i]; } int main(){ int T[N]; //辅助数组,即额外空间代价O(N) int a[]={,,,}; cnt = ; //初始cnt为0 mergeSort(a,,N,T); printf("逆序数为:%d\n",cnt); return ; }
结语
对比先前两重循环暴力求解逆序数的做法,可以证明归并求解的时间复杂度是O(NlgN)。因此,当N较大时,可以发现本归并算法的明显高效很多。