用归并排序求逆序对

相比树状数组求逆序对,归并排序的逻辑复杂度稍微小一点。


首先我们来理解归并排序。首先用mergeSort将一个序列不断二分,直到每个子序列只有长度2

然后递归到了栈底。我们再用merge函数,将递增有序的序列拼接起来。因为序列递增有序,所有时间复杂度为O( max(m+n) ),这里的m、n分别是两个序列的长度。加上二分,总的时间复杂度接近O(NlogN)

在拼接的过程中,用a、b分别记录两个序列的索引,然后不断取其中最小的。最后用两个while循环,将还满足“未越界”的a或b循环变量遍历完。最后,将临时数组放到arr工作数组中。


然后讨论怎样用这个过程求逆序数:

假设我们有两个递增有序的序列A和B:

A:1 3 5

B:2 4

循环取数的顺序是:1  3  5

加粗的表示取到了B序列。而这一过程中统计的逆序数的数目,就是A的循环下标a的右端数的数目。

这很好理解:3、5都比2(当前B序列的数)大,5比4(当前B序列的数)大

牢记以上原则,就可以编写出求逆序对的代码。

板子:

int arr[LEN];
int tmp[LEN];
int ans=0;

void merge(int s,int mid,int e){
    int i=0,a=s,b=mid+1,j=0;
    while(a<=mid && b<=e){
        if(arr[a]<=arr[b]){
            tmp[i++]=arr[a++];
        }else{
            tmp[i++]=arr[b++];
            ans+=mid-a+1;
        }
    }
    while(a<=mid) tmp[i++]=arr[a++];
    while(b<=e) tmp[i++]=arr[b++];
    FF(j,i) arr[s+j]=tmp[j];
}

void mergeSort(int s,int e){
    if(s<e){    //能够被划为。如果只有一个数就不能被划分 
        int mid=(s+e) /2;
        mergeSort(s,mid);
        mergeSort(mid+1,e);
        merge(s,mid,e);
    }
}
原文地址:https://www.cnblogs.com/TQCAI/p/8641452.html