C/C++ BM20 数组中的逆序对

前言

除了最常规的暴力破解,还要根据题意考虑更优质解答。


题目

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

数据范围:
对于 50% 的数据, s i z e ≤ 1 0 4 size≤10^4 size104
对于 100%100% 的数据, s i z e ≤ 10 size≤10%5 size10
数组中所有数字的值满足 0 ≤ v a l ≤ 1 0 9 0≤val≤10^9 0val109

要求:空间复杂度 O ( n ) O(n) O(n),时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
输入描述:
题目保证输入的数组中没有的相同的数字

示例1
输入: [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0 ] [1,2,3,4,5,6,7,0] [1,2,3,4,5,6,7,0]
返回值: 7 7 7

示例2
输入: [ 1 , 2 , 3 ] [1,2,3] [1,2,3]
返回值: 0 0 0

解决方案一

1.1 思路阐述

题目最简单的方式就是暴力破解,直接遍历一遍即可。

序列的每一个数和它后面的子序列中的每一个数进行比较,如果遇到小的数,则构成逆序对,否则就继续遍历。

两重循环即可搞定,这样做下来空间复杂度是 O ( 1 ) O(1) O(1)时间复杂度 O ( n 2 ) O(n^2) O(n2)

这里有一个注意的地方在于count的变量类型,因为最后的数据要对1000000007进行求余后输出,所以如果用int类型,那么最后的数据大小不够,所以要用64位的数据类型。这里用的是long int。

1.2 源码

int InversePairs(vector<int>& nums) {
    // write code here
    long int count=0;
    for (int i=0; i<nums.size(); i++) {

        for (int j=i+1; j<nums.size(); j++) {
            if(nums[i]>nums[j])
            {
                count++;
            }
        }
       
    }
    return count%1000000007;
}

解决方案二

2.1 思路阐述

对于解法一,这是最简单的 ,但是并不满足题目要求。
根据题目的要求提示,能看得出来这里最好用的是归并排序的方法。

这里引用一下官方的解答。

因为我们在归并排序过程中会将数组划分成最小为1个元素的子数组,然后依次比较子数组的每个元素的大小,依次取出较小的一个合并成大的子数组。

这里我们也可以用相同的方法划分,划分之后相邻一个元素的子数组就可以根据大小统计逆序对,而不断往上合并的时候,因为已经排好序了,我们逆序对可以往上累计。我们主要有以下三个阶段。

具体做法:

step 1: 划分阶段:将待划分区间从中点划分成两部分,两部分进入递归继续划分,直到子数组长度为1.
step 2: 排序阶段:使用归并排序递归地处理子序列,同时统计逆序对,因为在归并排序中,我们会依次比较相邻两组子数组各个元素的大小,并累计遇到的逆序情况。而对排好序的两组,右边大于左边时,它大于了左边的所有子序列,基于这个性质我们可以不用每次加1来统计,减少运算次数。
step 3: 合并阶段:将排好序的子序列合并,同时累加逆序对。

在这里插入图片描述

2.2 源码

class Solution {
  public:
    int mod = 1000000007;
    int mergeSort(int left, int right, vector<int>& data, vector<int>& temp) {
        //停止划分
        if (left >= right)
            return 0;
        //取中间
        int mid = (left + right) / 2;
        //左右划分合并
        int res = mergeSort(left, mid, data, temp) + mergeSort(mid + 1, right, data,
                  temp);
        //防止溢出
        res %= mod;
        int i = left, j = mid + 1;
        for (int k = left; k <= right; k++)
            temp[k] = data[k];
        for (int k = left; k <= right; k++) {
            if (i == mid + 1)
                data[k] = temp[j++];
            else if (j == right + 1 || temp[i] <= temp[j])
                data[k] = temp[i++];
            //左边比右边大,答案增加
            else {
                data[k] = temp[j++];
                //统计逆序对
                res += mid - i + 1;
            }
        }
        return res % mod;
    }
    int InversePairs(vector<int> data) {
        int n = data.size();
        vector<int> res(n);
        return mergeSort(0, n - 1, data, res);
    }
};

总结

最近整理了一下各种排序的算法,暂时还没整理到归并排序,所以这里第一优化的反应是考虑用二分查找的方式,但是二分查找一般涉及到有序的数列。这里还是不符合。

这里我们想要的是一种分治的思想,所以归并排序会更好。

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

澄澈i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值