递归O(NlgN)求解逆序数

导言

第一次了解到逆序数是在高等代数课程上。当时想计算一个数列的逆序数直觉就是用两重循环O(n^2)暴力求解。现在渐渐对归并算法有了一定的认识,因此决定自己用C++代码小试牛刀。

逆序数简介

由自然数12…,n组成的不重复的每一种有确定次序的排列,称为一个n级排列(简称为排列);或者一般的,n个互不同元素排成一列称为“一个n级排列”。例如,12344312都是4级排列,而24315是一个5级排列。

在一个n级排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个“逆序”。

例子:
  123成为自然排列 逆序数为 0
  324一列数 逆序排列有(32) (31) (21) (41) 所以逆序数是4

 

代码实现

 

#include <cstdio>
#include <cstring>

 

const int N = 4;   //测试数组的大小

int cnt;  //全局变量

 

void mergeSort(int *a,int p,int q,int *T){

    if(p+1>=q) return;

    int m=p+(q-p)/2;

    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[]={3,2,4,1};

    cnt = 0;  //初始cnt为0

    mergeSort(a,0,N,T);

    printf("逆序数为:%d
",cnt);

    return 0;

}

结语

对比先前两重循环暴力求解逆序数的做法,可以证明归并求解的时间复杂度是O(NlgN)。因此,当N较大时,可以发现本归并算法的明显高效很多。

原文地址:https://www.cnblogs.com/SeaSky0606/p/4567805.html