[Leedcode][JAVA][面试题51][数组中的逆序对][归并排序]

【问题描述】面试题51.数组中的逆序对 (困难)

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

输入: [7,5,6,4]
输出: 5
 
限制:
0 <= 数组长度 <= 50000

【解答思路】

1. 暴力 超时
  • 枚举所有数组
    -符合条件累加
    时间复杂度:O(N^2) 空间复杂度:O(1)
public int reversePairs(int[] nums) {
         int n = nums.length;
         int count = 0 ;
         if(n == 0){
             return 0;
         }
         for(int i = 0 ; i< n-1;i++){
            for(int j = i+1 ;j < n ;j++){
                if(nums[i]>nums[j]){
                    count++;
                }
            }    
         }
        return count;
    }
2. 归并排序
  • 合并1 前面有四个树比1大 总数+4



计数关键
count = mid - i -1

时间复杂度:O(NlogN) 空间复杂度:O(N)

 public int reversePairs(int[] nums) {
        int  len = nums.length;

        if(len <2 ){
            return  0;
        }

        int[] copy = new int[len];
        for (int i = 0; i < len ; i++) {
            copy[i] = nums[i];
        }
        int[] temp = new int[len];
//引入temp[] 为了避免每次合并时需要创建、销毁新的数组,避免下标问题
        return  reversePairs(copy,  0 ,len - 1, temp);
    }

    /**
     *
     * @param nums
     * @param left
     * @param right
     * @param temp
     * @return
     */
    private int reversePairs(int[] nums, int left, int right, int[] temp) {
        if(left == right){
            return 0;
        }
        //防止溢出
        int mid = left + (right- left) /2;
        int leftPairs = reversePairs(nums, left, mid , temp);
        int rightPairs = reversePairs(nums, mid+1, right, temp);
        //优化归并排序
        if(nums[mid] <= nums[mid+1]){
            return   leftPairs + rightPairs;
        }
    
        int crossPairs = mergeAndCount(nums, left, mid ,right, temp);
        return  leftPairs + rightPairs + crossPairs;
    }

    /**
     *
     * @param nums
     * @param left
     * @param mid
     * @param right
     * @param temp
     * @return
     */
    private int mergeAndCount(int[] nums, int left, int mid, int right, int[] temp) {
      //区间复制到temp[]数组
        for (int i = left; i <=right ; i++) {
            temp[i] =  nums[i];
        }

        int i = left ;
        int j = mid+1;
        int count = 0 ;
        for (int k = left; k <= right ; k++) {
            //前半部分遍历完
            if(i == mid+1){
                nums[k] = temp[j];
                j++;
            }//后半部分遍历完
             else if(j == right+1){
                nums[k] = temp[i];
                i++;
            }
            //count计数 逻辑(else if -else )   <= 稳定性 
            else if(temp[i]<=temp[j]){
                nums[k] = temp[i];
                i++;
            }else {
                nums[k] = temp[j];
                j++;
                count += (mid-i+1);
            }
            
        }
        return  count;
    }

【总结】

1. 归并排序思想
2. 归并排序优化
  if(nums[mid] <= nums[mid+1]){
            return   leftPairs + rightPairs;
        }
3. 规避排序模板
import java.util.Arrays;

public class MergeSort2 {

   // 用于合并两个有序数组的辅助数组,使用它是为了避免每次归并都重复开辟空间
   // 它的长度,就是数组的长度,每次只用它的一部分,直到最后一次归并,使用它的全部
   private int[] temp;

   // 自顶向下的归并排序实现 2
   public void sort(int[] arr) {
       int len = arr.length;
       temp = new int[len];
       mergeSort(arr, 0, len - 1);
   }

   private void mergeSort(int[] arr, int left, int right) {



       // 修复1:为了防止计算 int mid = (left + right) / 2; 整形溢出,应该像下面这样计算 mid
       int mid = left + (right - left) / 2;

       mergeSort(arr, left, mid);
       mergeSort(arr, mid + 1, right);

       // 优化2:如果 arr 数组已经前后有序(前面数组中的最后一个元素不大于后面数组中的第 1 个元素)
       if (arr[mid] <= arr[mid + 1]) {
           return;
       }

       mergeOfTwoSortArray(arr, left, mid, right);
   }

   private void mergeOfTwoSortArray(int[] arr, int left, int mid, int right) {
       // 优化3:使用一个全局的辅助数组,避免每次都开辟新的内存空间去优化,这样做反而编程实现更简单,不用考虑索引的偏移
       for (int i = left; i <= right; i++) { // 闭区间,所以 right 这个索引也要赋值
           temp[i] = arr[i];
       }
       // temp 的 [left,mid] 有序
       // temp 的 [mid+1,right] 有序,现在要将它们整理成有序,放回 arr 中,一个一个放就好了
       int i = left;
       int j = mid + 1;
       for (int k = left; k <= right; k++) {
           // i 用完
           if (i > mid) {
               arr[k] = temp[j];
               j++;
           } else if (j > right) {  // j 用完
               arr[k] = temp[i];
               i++;
           } else if (temp[i] < temp[j]) {
               arr[k] = temp[i];
               i++;
           } else {
               arr[k] = temp[j];
               j++;
           }
       }
   }

   public static void main(String[] args) {
       int[] nums = {8, 4, 3, 6, 5, 1};
       MergeSort2 mergeSort2 = new MergeSort2();
       mergeSort2.sort(nums);
       System.out.println(Arrays.toString(nums));
   }
}

参考网站:https://liweiwei1419.gitee.io/leetcode-algo/leetcode-by-tag/sorting-algorithm/merge-sorting.html#%E4%BC%98%E5%8C%96%E7%9A%84%E5%AE%9E%E7%8E%B0

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容