leetcode 51 数组中的逆序对

官方解法带视频讲解,推荐先看视频再来看本文的讲解

https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/shu-zu-zhong-de-ni-xu-dui-by-leetcode-solution/

 

采用归并思想的分治法

可以发现在逆序数组中如5-4-3-2-1中的逆序对为4+3+2+1

计算过程主要是在不同阶段的情况下不断计算,归并方法刚好是在不同的区间范围内进行的再排序,所以我们可以考虑归并方法

核心思想其实是将所有的数字不断二分,直到所有的长度均为1的大小的数组,然后逐渐向上进行归并,注意这里的逻辑是先进后出的思想。

由于两个有序的数组(A、B)进行归并的时候,分别从头开始向后遍历,将较小的放入数组中,且如果存在逆序就加上相应的逆序数值;

逆序数值的计算方法是如果A>B可以构成逆序,在将B放入数组的同时看B前面一共有多少个数字,数量即为此时的逆序数值,类似上面例子中的4+3+2+1中的3等。

注意

当两个较大的int数字求平均的时候容易溢出,所以用left+(right-left)/2的方法进行计算平均值。

count的计算依赖于计算索引间距。

循环以及函数边界的传递多为闭区间,尤其在循环的时候需要注意。

时空复杂度

  • 时间复杂度:O(NlogN)归并排序的时间复杂度
  • 空间复杂度:O(N)归并排序需要用到的临时数组,这里可能会有疑惑,为什么我们不是以排序为目的进行,能不能只要count计数逆序对,而不顾排序结果呢?答案是不能,因为我们的count的计算依赖于cross函数,也就是归并的过程,而下一次的归并也就是count的计算是有前提的也就是要求是两个有序的子数组,所以排序过程至关重要。
public class Solution {
    public int reversePairs(int[] nums) {
        int len=nums.length;
        if(len<2){//如果只有0个或1个说明不存在
            return 0;
        }
        int []copy=new int[len];
        for(int i=0;i<len;i++){
            copy[i]=nums[i];
        }
        int[] tmp=new int[len];
        return reversePairs(nums,0,len-1,tmp);
    }
    public int reversePairs(int[] nums,int left,int right,int[]tmp){
        if(left==right){
            return 0;
        }
        int mid=left+(right-left)/2;
        int leftnum=reversePairs( nums, left,mid,tmp);
        int rightnum=reversePairs(nums, mid+1,right,tmp);
        if(nums[mid]<=nums[mid+1]){//此时crossnum=0
            return leftnum+rightnum;
        }
        int crossnum=mergenum(nums, left,mid,right,tmp);
        return rightnum+leftnum+crossnum;
    }
    public int mergenum(int[] nums,int left,int mid,int right,int[]tmp){
        for(int i=left;i<=right;i++){
            tmp[i]=nums[i];
        }
        int i=left,j=mid+1;
        int count=0;
        for(int k=left;k<=right;k++){
            if(i==mid+1){//表示左边的遍历到末尾了
                nums[k]=tmp[j];
                j++;
            }else if (j==right+1){//表示右边的遍历到末尾了
                nums[k]=tmp[i];
                i++;
            }else if(tmp[i]<=tmp[j]){
                nums[k]=tmp[i];
                i++;
            }else{
                nums[k]=tmp[j];
                j++;
                count+=(mid-i+1);//这里需要注意不是自增1,而是要计算索引间距
            }
        }
        return count;
    }
 
}

 

posted @ 2020-05-09 23:48  我是SSP  阅读(195)  评论(0编辑  收藏  举报