数组中的逆序对--归并中的计数(分治)

0x01.问题

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

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

来自《剑指offer》

public int reversePairs(int[] nums)

0x02.解决思路

看问题,并不复杂,对于每一个数,只需要找出后面比它小的数的个数,最后累加起来,就是最终的答案,于是可以暴力的写出两层循环的代码,但是,这个时间复杂度达到了O(N^2),对于五位数的数组长度来说,已经无法在短时间内计算出来了,所以必须进行更高效的优化。

再来回想一下我们看到这个问题的第一想法,逆序,大于,这些字眼,都刺激着我们,似乎可以排序,但仔细去想一下排序的思路,排完序的时候,整个数组已经变了, 里面元素的关系也完全变了,而题目中的逆序,很明显是跟元素之间的先后位置有关的,所以,很快就打消了排序的思路。

再仔细想想,好像暂时没有更好的办法了,对于这个问题的话,扫描数组一遍的话肯定是不可能的,也就是说,O(N)的复杂度是不现实的,那么我们就需要想办法将这个复杂度降为介于O(N^2)O(N)复杂度,最容易想到的,那就是O(N*logN)。对于这种复杂度,要么递归,要么二分。到这里为止,最为符合的应该就是分治了,分而治之。

怎么进行分治了,发现问题还是回到了排序了,我们虽然不能直接进行排序解决这个问题,但我们可以利用归并排序的中间过程去计算数量,在这个中间过程,这个位置关系还是满足的。

我们回顾一下归并排序的具体做法,在合并的步骤中,应该是这样的:

在这里插入图片描述

  • 从左到右,依次比较一个元素,小者加入合并后的新数组。

在这个归并的过程中,我们是不是可以确定没一个元素和后面一个数组的大小关系,可以计算出比它大的数有多少,并且位置关系是完全满足的。

  • 可能你会有一个疑问,那么,在当前这个子数组中呢?位置不是已经变了,嘛,变成排序之后的了。

  • 你可能忘了归并是先分解到最小才开始排序的,也就是说,在数组中只有一个元素的时候,合并成这个数组后,这些数目已经计算进去了,并且是递归进去的。

在有了这条最基本的思路,我们可以去完善我们解决这个问题的大体思路了:

  • 模拟归并排序的过程,在每次排序的过程中,合成新数组之前,进行计数,计算符合题目要求的数组,最后累加返回。

0x03.解决代码–归并排序(分治)

class Solution {
    private int mergeSort(int[] nums,int[] temp,int left,int right){
        if(left>=right){
            return 0;
        }
        int mid=(left+right)/2;
        //分解的过程
        int count=mergeSort(nums,temp,left,mid)+mergeSort(nums,temp,mid+1,right);
        int i=left;//控制左边下标
        int j=mid+1;//控制右边下标
        int index=left;//控制新数组的下标
        while(i<=mid&&j<=right){
            if(nums[i]<=nums[j]){//左边的小
                temp[index++]=nums[i++];
                count+=j-(mid+1);//归并中符合条件的个数
            }else{//右边的小
                temp[index++]=nums[j++];
            }
        }
        //左边有剩余
        for(int k=i;k<=mid;k++){
            temp[index++]=nums[k];
            count+=j-(mid+1);//每一个数都满足条件
        }
        //右边有剩余
        for(int k=j;k<=right;k++){
            temp[index++]=nums[k];
        }
        //将已完成的temp拷贝到nums
        for (int k = left; k <=right; k++){
            nums[left+k-left] = temp[k];
        }
        return count;
    }
    public int reversePairs(int[] nums) {
        int n=nums.length;
        int[] temp=new int[n];//归并过程中需要的新数组
        return mergeSort(nums,temp,0,n-1);
    }
}

ATFWUS --Writing By 2020–04-24

  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ATFWUS

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

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

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

打赏作者

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

抵扣说明:

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

余额充值