剑指offer:数组中的逆序对

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
解法一:一个数字能不能构成逆序对,关键看后面有几个比他小的数字。根据这个思路,我们可以从后向前遍历整个数组。并用一个大小为10的数组,分别来保存从后向前遍历数组时0-9每个数字出现的次数。在遍历数组的每一个元素时,就可以算出这个元素对应的逆序对的个数。这样就能算出数组中逆序对的个数。

这个算法的空间复杂度为O(1),因为只需要长度为10的数组。时间复杂度为O(10n),每次遍历一个元素,要统计数组中位于这个元素后面比他小的元素的个数,因此是10n。在n>1024时,本算法优于二分算法解决此问题(二分算法解决此问题的时间复杂度为O(nlogn),并且二分算法的空间复杂度为O(n))。


int InversePairs(vector<int> data) {
        //如果数组为空,返回逆序对数为0
        if (data.empty()){
            return 0;
        }
        //从后向前遍历数组中的元素,同时用一个数组(哈希表)保存每个数字出现的次数
        //每访问数组中的一个元素,就统计逆序对的个数
        int inverse = 0;
        vector<int> numbers(10, 0);
        for (vector<int>::iterator ite = data.end() - 1; ite >= data.begin(); ite--){
            int index = *ite;
            inverse += sum(numbers, index);
            numbers[index]++;
        }
        return inverse;
    }
    int sum(vector<int> &numbers, int &index){
        int s = 0;
        for (int i = 0; i < index; i++){
            s += numbers[i];
        }
        return s;
    }


解法二:剑指offer上指出,采用二分方法求解数组中的逆序对。

将数组分成两个子数组,分别求出这两个子数组的逆序对,并同时对这两个子数组排序。然后求出整个子数组的逆序对,并对整个子数组排序。


此算法的时间复杂度为O(nlogn),空间复杂度为O(n)。


int InversePairs(vector<int> data) {
        if (data.empty()){
            return 0;
        }
        int inverse = 0;
        vector<int> tmpdata(data.size(), 0);
        InversePairs(data, tmpdata, 0, data.size() - 1, &inverse);
        return inverse;
    }
    void InversePairs(vector<int> &data, vector<int> &tmp, int begin, int end, int *times){
        if (begin == end){
            return;
        }
        int mid = (begin + end) / 2;
        InversePairs(data, tmp, begin, mid, times);
        InversePairs(data, tmp, mid + 1, end, times);
        
        int first, second, index;
        first = mid;
        second = end;
        index = end;
        
        while ((first >= begin) && (second >= mid + 1)){
            if (data[first] > data[second]){
                tmp[index] = data[first];
                (*times) = (*times) + (second - mid);
                first--;
                index--;
            }
            else {
                tmp[index] = data[second];
                second--;
                index--;
            }
        }
        while (first >= begin){
            tmp[index] = data[first];
            first--;
            index--;
        }
        while (second >= mid + 1){
            tmp[index] = data[second];
            index--;
            second--;
        }
        for (int i = begin; i <= end; i++){
            data[i] = tmp[i];
        }
       return;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值