數組中的逆序對--歸併中的計數(分治)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章