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

在一個數組中,每一個數左邊比當前數小的數累加起來,叫做這個數組的小和。求一個數組的小和。
例子:
[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)

(時間複雜度的詳細計算和歸併詳細圖解:https://blog.csdn.net/u010452388/article/details/81008727

圖解流程

下圖爲歸併排序的過程,將數據分解後,再進行合併

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

累加的原理

這裏主要就是利用合併的過程中,兩個有序組都是有序的進行判斷累加,我們以上圖的數據爲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;
    }

 

 

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