1. 程式人生 > >歸併排序解決小和問題 (圖解詳細流程)

歸併排序解決小和問題 (圖解詳細流程)

在一個數組中,每一個數左邊比當前數小的數累加起來,叫做這個陣列的小和。求一個數組的小和。
例子:
[1,3,4,2,5]
1左邊比1小的數,沒有;
3左邊比3小的數,1;
4左邊比4小的數,1、3;
2左邊比2小的數,1;
5左邊比5小的數,1、3、4、2;
所以小和為1+1+3+1+1+3+4+2=16

目錄

笨辦法

程式碼

圖解流程

累加的原理

程式碼

笨辦法

迴圈遍歷每個數的左邊的數與當前數進行比較,如果比當前數小,則累加起來,第一次遍歷1次,第二次遍歷2次,第n次遍歷n次,是一個等差數列,但是時間複雜度為O(N^2)

程式碼

    public static int smallSum(int[] arr) {
        //宣告累加變數
        int res=0;
        for (int i = 1; i < arr.length; i++) {
            //遍歷索引小於i的元素,並進行判斷
            for (int j = 0; j < i; j++) {
                if(arr[j]<arr[i]){
                    res+=arr[j];
                }
            }
        }
        return res;
    }

歸併排序解決

通過歸併排序可以將時間複雜度控制到O(N*logN)

圖解流程

下圖為歸併排序的過程,將資料分解後,再進行合併

原理:歸併的每一次合併都是將兩個有序組合併為一個有序組,合併好後的有序組,再和另外的有序組繼續合併,最終可以得到一個完整的有序陣列

累加的原理

這裡主要就是利用合併的過程中,兩個有序組都是有序的進行判斷累加,我們以上圖的資料為3,5組和資料為8,9組合並的過程為例,來計算累加的結果

從上面的圖可以看出,如果p1索引的值小於p2索引的值,那麼這一次排序的過程可以計算右側陣列比3大的數有2個(因為每一組都是有序的),然後索引p1向右移動

從上面的圖可以看出,p1索引的值小於p2索引的值,那麼這一次排序過程可以計算出右邊比5大的數有2個

總結:上面兩個有序組合併為一個有序組時,累加的小和的值為: 3*2+5*2=16

程式碼

    public static int smallSum(int[] arr) {
        if (arr == null || arr.length < 2) {
            return 0;
        }
        return  mergeSort(arr,0,arr.length-1);
    }

    public static int mergeSort(int[] arr, int L, int R) {
        if (L == R) {
            return 0;
        }
        int mid = (L + R) >>>1;//這裡是防止資料溢位
        return mergeSort(arr, L, mid) + mergeSort(arr, mid + 1, R) + merge(arr, L, mid, R);
    }
    //合併的過程
    public static int merge(int[] arr, int L, int mid, int R) {
        //準備一個臨時陣列,長度和傳進來的arr一樣
        int[] temp = new int[R - L + 1];
        int p1 = L;
        int p2 = mid + 1;
        //臨時陣列temp的索引起始變數
        int i = 0;
        //小和結果的變數
        int result = 0;
        //合併陣列的迴圈,並計算小和
        while (p1 <= mid && p2 <= R) {
            if (arr[p1] < arr[p2]) {
                //計算小和的累加結果,(R-p2+1)為比數arr[p1]大的數量
                result += (R - p2 + 1) * arr[p1];
                temp[i] = arr[p1];
                p1++;
                i++;
            } else {
                temp[i] = arr[p2];
                p2++;
                i++;
            }
        }
        while (p1 <= mid) {
            temp[i] = arr[p1];
            i++;
            p1++;
        }
        while (p2 <= R) {
            temp[i] = arr[p2];
            i++;
            p2++;
        }
        //這裡是將臨時陣列temp的元素重新賦值給傳入進來的arr
        for (int j = 0; j < temp.length; j++) {
            arr[L + j] = temp[j];
        }
        return result;
    }