利用归并排序求逆序对

在逆序对的问题中,如果采用暴力求解的方法,一般也是有效的,但是O(n2)时间复杂度实在是难以接受的。但是对于逆序对问题,却有一个看似不想关的算法来解决–归并排序。时间复杂度和空间复杂度完全与归并排序一样,只是在归并过程中,添加了一个变量,对于逆序对的数目进行了记录。这样就将时间复杂度降低到了O(nlogn)。

原理

因为逆序对的数目可能存在平方数个逆序对,因此要想将逆序对数目求解的复杂度降低到O(nlogn),就不能对每一个逆序对进行计算。根据归并排序思想,使用归并求解逆序对时,将会在将两个有序数组合并成一个有序数组的过程中,记录逆序对的数目,其他如数组划分和排序过程同归并排序。

在归并排序的合并步骤中,假设将两个有序数组A[] 和有序数组B[] 和并为一个有序数组C[]。计算逆序对问题转换为计算逆序对(a,b)的问题,其中a来自A[], b来自B[]。当a < b的时候,不计数,当a>b的时候(a,b)就是逆序对,由于A[]是有序的,那么A[]中位于a之后的元素对于B[]中的元素b也形成了逆序对,于是对于逆序对(a,b),(假设A[]的起始下标为sa,结束下标为ea,a的下标为pos)实际上合并成C[]后会会产生ea-pos+1个逆序对。

这里写图片描述

也就是说,合并过程中,每次出现一对这样的(a,b),逆序对数目sum = sum + ea-pos+1 ;
根据这样的原理,再给予对归并排序的理解,将上面的计算公式加入到归并排序中,就可以在O(nlogn)的时间复杂度里计算出一个给定数字序列中逆序对的数目。

示例代码


    #include<iostream>

    #define N 100000

    using namespace std;

    void merge(int arr[],int start,int mid,int end,int temp[],long long *count){
        int index = 0,index1 = 0,index2 = 0;
        index = 0; index1 = start; index2 = mid+1;

        while((index1<=mid) && (index2<=end)){
            if(arr[index1] <= arr[index2]){
                temp[index++] = arr[index1++];
            }
            else{
                temp[index++] = arr[index2++];
                //ans+=e1-p1+1;
                (*count) = (*count) + mid - index1 + 1;
            }
        }
        while(index1<=mid)
            temp[index++] = arr[index1++];
        while(index2<=end)
            temp[index++] = arr[index2++];

        for(int i=0;i<index;i++){
            // 复制也是递归进行的,所以并不是从start开始到end
            arr[start+i] = temp[i];
        }
    }

    void mergeSort(int arr[],int start,int end,int temp[],long long *count){
        if(start<end){ // 递归出口
            int mid = 0;
            mid = (start+end)>>1;
            mergeSort(arr,start,mid,temp,count);
            mergeSort(arr,mid+1,end,temp,count);
            merge(arr,start,mid,end,temp,count);
        }
    }
    int main(){
        long long count = 0;
        int m = 0;
        cin>>m;
        int *a = new int[m];
        for(int i=0;i<m;i++){
            cin>>a[i];
        }

        int *temp = new int[m];
        mergeSort(a,0,m-1,temp,&count);
        delete []a;
        delete []temp;
        cout<<count<<endl;

        return 0;
    }

输入格式为:先输入一个数字代表数组的长度,回车,接下来依次输入以空格分割的n个数组元素。输出就是该数组的逆序对数目。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值