一刷剑指offer(36)——数组中的逆序对

题目:
在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对总数。
例如数组{7,5,6,4|中,存在5个逆序对:(7,6),(7,5),(7,4),(6,4),(5,4)。

最直接的做法:顺序扫描整个数组,每扫描到一个数字,逐个比较该数字和它后面数字的大小。如果后面数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有n个数字,由于每个数字都要和O(n)个数字比较,因此这个算法事件复杂度为O(n^2)。

考虑比较相邻数字:以{7,5,6,4}为例

依次把数组分解成长度为n/2,n/4...1的子数组。

在第一对长度为1的子数组{7}、{5}中7>5,因此(7,5)组成一个逆序对。

在第二对长度为1的子数组{6}、{4}中6>4,因此(6,4)组成一个逆序对。

由于已经统计了两对子数组内部的逆序对,因此需要把这两对子数组排序,以免重复统计。

用两个指针分别指向两个子数组的末尾,并每次比较两个指针指向的数字。

如果P1>P2,那么构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的数目。

如果P1<=P2,那么不构成逆序对。

每一次比较的时候,都会把较大的数字从后往前复制到一个辅助数组中,确保辅助数组中的数字是递增排序的。

在把较大的数字复制到辅助数组后,把相应指针向前移动一位,接下来进行下一步比较。

int InversePairsCore(int* data,int* copy,int start,int end)
{
    if(start==end)
    {
        copy[start]=data[start];
        return 0;
    }
    int length=(end-start)/2;
    int left=InversePairsCore(copy,data,start,start+length);
    int right=InversePairsCore(copy,data,start+length+1,end);
    //i初始化为前半段最后一个数字的下标
    int i=start+length;
    //j初始化为后半段最后一个数字的下标
    int j=end;
    int indexCopy=end;
    int count=0;
    while(i>=start && j>=start+length+1)
    {
        if(data[i]>data[j])
        {
            copy[indexCopy--]=data[i--];
            count+=j-start-length;
        }
        else
        {
            copy[indexCopy--]=data[j--];
        }
    }
    for(;i>=start;--i)
        copy[indexCopy--]=data[i];
    for(;j>=start+length+1;--j)
        copy[indexCopy--]=data[j];
    return left+right+count;
}

int InversePairs(int* data,int length)
{
    if(data==NULL ||length<0)
        return 0;
    int* copy=new int[length];
    for(int i=0;i<length;i++)
        copy[i]=data[i];
    int count=InversePairsCore(data,copy,0,length-1);
    delete[] copy;
    return count;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值