zoukankan      html  css  js  c++  java
  • 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;
        }
     
    }
  • 相关阅读:
    Java初始化顺序
    生产者与消费者
    Java线程
    思科QoS知识全面了解
    思科3750交换机堆叠配置指南
    Cisco VRRP Config Case1
    思科交换机快速定位流量异常端口
    QoS笔记
    Cisco BGP团体配置要领
    Cisco BGP Local_Pref配置要领
  • 原文地址:https://www.cnblogs.com/sjh-dora/p/12861253.html
Copyright © 2011-2022 走看看