《剑指offer》面试题51. 数组中的逆序对

问题描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:

输入: [7,5,6,4]
输出: 5
 

限制:

0 <= 数组长度 <= 50000

这道题和leetcode 315.计算后面较小数字的个数一样。

代码

首先给一个超出时间限制的暴力解法:

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size(),i,j,ans=0;
        for(i = 0; i < n; ++i)
        {
            for(j = i+1; j < n; ++j)
            {
                if(nums[i] > nums[j])
                    ++ans;
            }
        }
        return ans;
    }
};

代码

插入排序,很奇怪,还是超出时间限制

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size(),i,ans=0,left,right,middle;
       vector<int> tmp;//里面的元素
       for(i = n-1; i >= 0; --i)
       {
           left = 0;right = tmp.size();
           while(left < right)
           {
               middle = left + (right - left)/2;
               if(nums[i] > tmp[middle])
               {
                   left = middle + 1;
               }
               else{
                   right = middle;
               }
           }
           ans += right;
           tmp.insert(tmp.begin()+right,nums[i]);
       }
        return ans;
    }
};

代码

归并排序:

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        if(nums.size() < 2)return 0;
        vector<int> tmp(nums.size());
        return merge(nums,0,nums.size()-1,tmp);
    }
    int merge(vector<int>& nums,int start,int end,vector<int>& tmp)
    {
        if(start >= end)return 0;
        int middle = start + (end - start)/2;
        int count = merge(nums,start,middle,tmp) + merge(nums,middle+1,end,tmp);
        int left = start,right = middle + 1,i= 0;
        //vector<int> tmp(end-start+1);//每次都申请空间浪费时间
        while(left <= middle && right <= end)
        {
            if(nums[left] > nums[right])
            {
                tmp[i++] = nums[left++];
                //如果没有下面的计数就是一个归并排序的操作(从大到小),因为左右两边要合并的数组各自都是从大到小排好序的数组了,一旦出现左边当前数比右边当前数大,则也比右边当前数后面的数都要大,都需要计入逆序
                count += end-right+1;
            }
            else{
                tmp[i++] = nums[right++];
            }
        }
        while(left <= middle)tmp[i++] = nums[left++];
        while(right <= end)tmp[i++] = nums[right++];
        for(i = 0; i < end-start+1; ++i)
            nums[start+i] = tmp[i];
        return count;
    }
};

结果

执行用时 :280 ms, 在所有 C++ 提交中击败了78.32%的用户
内存消耗 :44.4 MB, 在所有 C++ 提交中击败了100.00%的用户
posted @ 2020-05-31 08:25  曲径通霄  阅读(107)  评论(0编辑  收藏  举报