求數組中的逆序對 分治法


題目分析:先求前面一半數組的逆序數,再求後面一半數組的逆序數,然後求前面一半數組比後面一半數組中大的數的個數(也就是逆序數),這三個過程加起來就是整體的逆序數目了。這類似與歸併排序,歸併排序的思想就是把前一段排序,後一段排序,然後再整體排序。而且,歸併排序的規程中,需要判斷前一半數組和後一半數組中當前數字的大小。這也就是剛剛描述的逆序的判斷過程了。如果前一半數組的當前數字大於後一半數組的當前數字,那麼這就是一個逆序數。利用歸併排序的過程中,在每一次歸併兩個數組的時候,如果左數組比右數組大,那麼着就是一個逆序。記錄所有左數組比右數組大的情況,就是全部的逆序數目。

具體解析:

               圖片來自於兼職offer

先把數組分解成兩個長度爲2的子數組,再把這兩個子數組分解成兩個長度爲1的子數組。接下來一邊合併相鄰的子數組,一邊統計逆序對的數目。在第一對長度爲1的子數組{7}{5}7>5,因此(7,5)組成一個逆序對。同樣在第二對長度爲1的子數組{6}{4}中也有逆序對(6,4),由於已經統計了這兩對子數組內部的逆序對,因此需要把這兩對子數組進行排序,避免在之後的統計過程中重複統計。

逆序對的總數=左邊數組中的逆序對的數量+右邊數組中逆序對的數量+左右結合成新的順序數組時中出現的逆序對的數量;

//數組中的逆序對

    public static int InversePairs(int[] array){

        if(array==null||array.length<=1)

            return 0;

        int[] copy = new int[array.length];

        for(int i=0;i<array.length;i++){

            copy[i] = array[i];

        }

        return mergeCount(array, copy, 0, array.length-1);

    }

    

    public static int mergeCount(int[] array, int[] copy, int start, int end){

        if(start==end){

            copy[start] = array[start];

            return 0;

        }

        int mid = (start+end)>>1;

        int leftCount = mergeCount(copy, array, start, mid);

        int rightCount = mergeCount(copy, array, mid+1, end);

        

        int i = mid;//i初始化爲前半段最後一個數字的下標

        int j = end;//j初始化爲後半段最後一個數字的下標

        int index = end;//輔助數組複製的數組的最後一個數字的下標

        int count = 0;//計數--逆序對的數目

        while(i>=start&&j>=mid+1){

            if(array[i]>array[j]){

                copy[index--] = array[i--];

                count += j-mid;

            }else{

                copy[index--] = array[j--];

            }

        }

        for(;i>=start;i--){

            copy[index--] = array[i];

        }

        for(;j>=mid+1;j--){

            copy[index--] = array[j];

        }

        return leftCount+rightCount+count;

    }

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