数组中的逆序对

剑指OFFER题48------按牛客网热度排序

时间:2019.1.18.2113
作者:Waitt

题目

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

时间限制:2秒 空间限制:32768K 热度指数:236829

输入描述:

题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5

示例1

输入:
1,2,3,4,5,6,7,0
输出:
7

解答

思路1

可以认为是在冒泡排序的过程中,前面数字比后面数字大的次数,即交换的次数。因此可以选择用冒泡排序统计次数,再取模。
但是这种算法时间复杂度较高,超过了本题的要求,当数组比较大时,就会超时。

class Solution {
public:
    int InversePairs(vector<int> data) {
        int n=data.size();
        if(n<2)
            return 0;
        int c=0;
        bool hu=1;//标志位,防止多余交换判断
        for(int i=0;i<n&&hu;i++)//从头到尾
        {
            hu=0;//标志位置零
            for(int j=n-1;j>i;j--)//从尾到当前位
            {
                if(data[j]<data[j-1])
                {
                    int t=data[j];
                    data[j]=data[j-1];
                    data[j-1]=t;
                    c++;
                    hu=1;//若本轮存在交换数字,则将标志位置1
                }
            }
        }
        return c%1000000007;
    }
};

该算法为优化的冒泡算法,但复杂度仍比较高,通过率仅为50%。

思路2

由于此题可以看做是排序过程中数字的变化情况。
而对于排序:

冒泡排序,简单选择排序和直接插入排序,均为简单算法,时间复杂度也较高,均为O(n2)。
希尔排序(改进版插入排序)时间复杂度O(n3/2)。也算是比较高的复杂度。
堆排序,一般使用堆均为标准库的堆,若自己建立,过程较为麻烦,时间复杂度O(nlogn)。
归并排序,时间复杂度O(nlogn)。可取。
快速排序,在某些情况下,时间复杂度仍较高,可尝试。

本思路为采取归并排序,并记录其中的变化情况(逆序对)。
思路详解:https://www.nowcoder.com/questionTerminal/96bd6684e04a44eb80e6a68efc0ec6c5
注意:此题的逆序对的数目可能很大,因此定义计数位不能简单的定义为int

class Solution {
public:
    void guib(vector<int> &data,vector<int> &cp,int s,int n,long long &c)
    //归并排序函数头
    {
        if(s==n)
        {
            cp[s]=data[s];
            return;
        }
        int m=(s+n)/2;//取中分割
        guib(data,cp,s,m,c);//分割为前一半
        guib(data,cp,m+1,n,c);//分割为后一般
        int i=s,j=m+1;//i为前一半数组计数,j为后一半数组计数
        int k=s;//k为合并后的数组计数
        for(;i<=m&&j<=n;k++)//进行前后两个数组的合并
        {
            if(data[i]<=data[j])//前侧数字小于后侧数字时,说明无逆序对
            {
                cp[k]=data[i];
                ++i;
            }
            else//后侧数字小于前侧数字时,说明有逆序对,且逆序对数目为m-i+1;i为当前前侧所剩的数字个数
            {
                cp[k]=data[j];
                ++j;
                c=c+m-i+1;
            }
        }
        for(;i<=m;k++)//将剩余未合并的数字合并
        {
            cp[k]=data[i];
            ++i;
        }
        for(;j<=n;k++)//将剩余未合并的数字合并
        {
            cp[k]=data[j];
            ++j;
        }
        for(int l=s;l<=n;l++)//将cp赋值给data,才可继续进行循环
        {
            data[l]=cp[l];
        }
    }
    int InversePairs(vector<int> data) {
        int n=data.size();
        if(n<2)
            return 0;
        long long c=0;
        //由于此处的c可能非常大,会超出INT的范围,因此定义类型为long long,防止初始值不定,必须赋值为0
        vector<int> cp(data);//data为辅助向量
        guib(data,cp,0,n-1,c);
        return c%1000000007;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值