剑指OFFER-数组中的逆序对

Question

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

题目保证输入的数组中没有的相同的数字
数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

关键词:逆序对 归并排序

Solution

思路过程:
暴力破解就是每次用一个数字去比较剩下所有数字,这样时间复杂度是O(n^2),估计至少一半的数据会超时(AC25%)。
继续思考,如果从后往前,比如[3,1,2]这个数组,先对最后两个找到逆序对=0,排好这两个[2,1],然后3跟排好的[2,1]挨个儿比较(这里的数组已经排好序了,可以用折半查找来做减少时间),遇到第一个比他小的,那么剩下所有的([1])都比他小,逆序对加剩下的长度(=1)。这样理论上,最大的时间复杂度是N^2
(开始我记错了折半查找的复杂度,以为是log(N),其实是N^2,因为还要加上数组移动的时间,可恶),不过需要额外的空间保存排序好的数组。
这个方法仍然没有通过(AC50%),姑且写一个python版本的在最后。

好多人是使用了归并排序算法,相当于第二种思路一开始折半来排序,数组所有逆序对 = 左半边的逆序对 + 右半边的逆序对 + 左右之间的逆序对。前两个部分递归完成,最后一个部分由于左右也是排完序返回,所以也可以拿指针找到右边第一个大于他的数字,后面的长度就是该指针的逆序对。其实这是一个完整的归并排序。结果这个算法还是没有通过(AC75%) : )。
参考:https://www.jianshu.com/p/bbbab7fa77a2

归并排序

时间复杂度:O(N·log(N))
空间复杂度:O(N)

  • Python
class Solution:
    def InversePairs(self, data):
        ans, new_data = self.BinarySearch(data)
        return ans%1000000007
    
    def BinarySearch(self, data):
        if len(data)<2:
            return 0, data
        mid = len(data)//2
        left, new_left = self.BinarySearch(data[:mid])
        right, new_right = self.BinarySearch(data[mid:])
        merge, new_data = self.MergeSearch(new_left,new_right)
        return left + right + merge, new_data
    
    def MergeSearch(self, a, b):
        new_data = []
        count = 0
        i, j = 0, 0
        while i<len(a) and j<len(b):
            if a[i]<=b[j]:
                new_data.append(a[i])
                i += 1
            else:
                new_data.append(b[j])
                count += len(a) - i
                j += 1
        new_data += a[i:] + b[j:]
        return count, new_data
  • C++
class Solution {
public:
    int InversePairs(vector<int> data) {
        int ans = MergeSort(data, 0, data.size()-1);
        return ans%1000000007;
    }
    int MergeSort(vector<int> &data, int start, int end){
        if(start >= end)return 0;
        int mid = (start+end)/2;
        int left_count  = MergeSort(data, start, mid);
        int right_count = MergeSort(data, mid+1, end);
        //MergeOne(data, start, mid, end);
        int merge_count = 0;
        vector<int> temp;
        int k=0, i=start, j=mid+1;
        
        while(i<=mid && j<= end){
            if(data[i] <= data[j]){
                temp.push_back(data[i]);
                i++;
            }
            else{
                temp.push_back(data[j]);
                merge_count = merge_count + mid-i+1;
                j++;
            }
        }
        while(i<= mid){
            temp.push_back(data[i]);
            i++;
        }
        while(j<=end){
            temp.push_back(data[j]);
            j++;
        }
        for(int l=0; l<temp.size(); l++){
            data[start+l] = temp[l];
        }
        return left_count+right_count+merge_count;
    }
};

插入排序(未通过,待改)

时间复杂度:O(N*log(N))
空间复杂度:O(N)

  • Python
class Solution:
    def InversePairs(self, data):
        if len(data)<2:
            return 0
        count = 0
        if data[-1] > data[-2]:
            sort_d = [data[-1], data[-2]]
        else:
            sort_d = [data[-2], data[-1]]
            count += 1
        for i in range(3, len(data)+1):

            temp, sort_d = self.find(data[-i], sort_d)

            count += temp
        return count
    
    def find(self, x, d):
        left = 0
        right = len(d)-1
        while left<right:
            
            mid = (left+right)//2

            if d[mid]>x and d[mid+1]<x:
                return len(d)-mid-1, d[:mid+1] + [x] + d[mid+1:]
            if d[mid-1]>x and d[mid]<x:
                return len(d)-mid, d[:mid] + [x] + d[mid:]
            if d[mid]>x:
                left = mid+1
            else:
                right = mid
        
        if left==0:
            return len(d), [x] + d[:]
        if right==len(d)-1:
            return 0, d[:] + [x]

        return len(d)-right-1, d[:right+1] + [x] + d[right+1:]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值