LeetCode: 51. 数组中的逆序对

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

 

示例 1:

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

限制:

0 <= 数组长度 <= 50000

分析:

       暴力的方法很好想,但是难度是hard,而且数组长度是50000,可见暴力法必定超时。这里用的是分治法来降低时间复杂度。官方题解的视频已经讲得十分清晰明了了,我也不再班门弄斧,还是看官方题解吧。

       这里只是稍微补充一下:我自己是没有想到题解中的思路的,只是看了题解之后自己把代码敲了出来。这个题的暴力法是显而易见的,但是对于第一次面对这个题目的人而言,如何优化?视频中直接提出了合并两个顺序数组,计算其逆序数。个人觉得从暴力到合并,这之间的跨度有些大。我在整理思路的时候,认为应该从此处入手:逆序数实质上是两个数之间大小关系的一种表示,暴力法就是枚举了任意两个数之间的大小关系,如果前者大于后者,则计入在内;否则略过。要想提高效率,要注意到大小关系是具有传递性的。比如说[5,4,2,6,3],与5有关的逆序数怎么计算,可以看一下与4有关的逆序数是2(4 > 2, 4 > 3),5 > 4的话必然有5 > 2, 5 > 3,由此看来应当是可以利用传递性进行优化的,传递性是需要数与数之间是有序的。同时降低时间复杂度的话,不出意外应该是O(nlogn)的算法,这个时候一般能想到分治。此时我们可以考虑一下,在分治的时候使用有序数组合并是否可行。而这个在视频里讲解的很清楚。

       个人感觉这样思考的话,可能过渡更平缓一些,更容易让人接受。

class Solution {
public:
    int cnt = 0;
    void merge(int left, int mid, int right, vector<int> &nums){
        vector<int> backup(right - left + 1);
        int i = left, j = mid + 1, k = 0;
        while(i <= mid && j <= right){
            if(nums[i] <= nums[j]){
                backup[k++] = nums[i++];
            }else{
                backup[k++] = nums[j++];
                cnt += mid - i + 1;
            }
        }
        while(i <= mid) backup[k++] = nums[i++];
        while(j <= right) backup[k++] = nums[j++];
        for(int i = left, k = 0; i <= right; i++, k++)
            nums[i] = backup[k];
    }

    void merge_sort(int left, int right, vector<int> &nums){
        if(left < right){
            int mid = (left + right) / 2;
            merge_sort(left, mid, nums);
            merge_sort(mid + 1, right, nums);
            merge(left, mid, right, nums);
        }
    }

    int reversePairs(vector<int>& nums) {
        merge_sort(0, nums.size() - 1, nums);
        return cnt;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值