剑指offer T51数组中的逆序对

case1:暴力法

class Solution {
    /*
    case1:暴力法
    */
    public int reversePairs(int[] nums) {
     int len = nums.length;
     if(len<=0) return 0;
     int res = 0;
     for(int i=0;i

case2:
思想:借助二路归并排序
整个区间上的逆序对数由三部分构成:两个子区间上的逆序对(再归并生成生成这两个子区间时统计得到):(左区间[start,mid]上的逆序对+右区间[mid+1,end]上的逆序对)+[start,end]上跨区间的逆序对(归并左右两个子区间时统计得到)

【具体步骤】
先分治,然后合并,只不过再合并两个有序数组时同时统计逆序对的个数,且在合并时可以加一个判定用于统计逆序对的个数也就是当left指针所指向的的那个数nums[left]比right指针所指向的数nums[right]大时(也就是当要归并的数为nums[right]时去统计次数逆序对的个数),表明左边那个有序数组的全部数都比nums[right]大,这不就说明左边那个有序数组的每个数都与nums[right]构成一个逆序对,此时逆序对的个数应该为mid-left+1个
然后等到合并操作完成时,跨区间的逆序对(分别由左右两个区间上的数所构成的逆序对)就统计完了
然后再加个左右两个区间各自的逆序对数就是整个大区间上的逆序对个数了

时间复杂度:O(NlogN)
空间复制度:O(N)(不考虑递归使用的栈的深度)

class Solution {
    public int mergeAndCount(int[]nums,int start,int end,List temp){//temp用于暂存归并的结果,方便后续把归并后的结果给映射到nums[]
     int res = 0;
     int mid = (start+end)/2;
     //要归并的两个有序区间分别为[start,mid],[mid+1,end]
     int left = start;
     int right = mid+1;
     while(left<=mid&&right<=end){
         //nums[left]比nums[right]大时,表明左边那个有序数组的全部数都比nums[right]大,这不就说明左边那个有序数组的每个数都与nums[right]构成一个逆序对
         if(nums[left]>nums[right]){
             temp.add(nums[right]);
             //统计逆序对个数
             res+=(mid-left+1);
             right++;
         }else{
             //注重这里不再统计逆序对个数,要不会有重复
             temp.add(nums[left]);
             left++;
         }
     }
     //归并剩下的部分
     while(left<=mid){
         temp.add(nums[left++]);
     }
     while(right<=end){
         temp.add(nums[right++]);
     }
     //将归并后的有序结果反馈到nums[]中
     for(int i=0;i temp = new ArrayList<>();
       return mergeAndCount(nums,start,end,temp)+leftPairsCount+rightPairsCount; 
    } 

    public int reversePairs(int[] nums) {
     int len = nums.length;
     if(len<2) return 0;
     return dividen(nums,0,len-1);   
    }
}

你可能感兴趣的:(算法)