1. 程式人生 > >【算法導論 in lambda】並歸排序

【算法導論 in lambda】並歸排序

導論 [] 9.png emp 分拆 對象 只需要 內容 麻煩

並歸排序的過程就是一個先拆再合並的過程,先拆到全是不能再拆的最小數組,然後組與組之間合並,排序的過程在合並的過程中執行。

所以整個算法分兩部分,split和merge

先說merge吧,將兩個數組合並為新數組,符合從大到小。

 public int[] merge(int[] c1, int[] c2) {
        int[] ret = new int[c1.length + c2.length];
        for (int i = 0, j = 0, index = 0; i < c1.length || j < c2.length; ) {
            if (i < c1.length) {
                if (j < c2.length) {
                    if (c1[i] >= c2[j]) {
                        ret[index++] = c2[j++];
                    } else {
                        ret[index++] = c1[i++];
                    }
                } else {
                    ret[index++] = c1[i++];
                }
            } else {
                ret[index++] = c2[j++];
            }
        }
        return ret;
    }

  鄧老師的教案給出過另外一種復雜校驗的版本,不過其教案上也註明了,從效率的角度而言,還是這樣拆開來寫比較好。。。

  merge方法的條件是兩個已經排好序的數組c1和c2,返回值則是也已經排好序的包括c1和c2所有內容的另一數組。

  

  其中是有一個for循環來著,邏輯上可以用IntStream.iterate來代替,但代碼中對c1,c2,ret的操作都是尋秩訪問,IntStream.iterate的執行過程大多是對指針的操作而非數據本身,就很麻煩。。不知道咋寫。

  split方法需要執行的操作是將數組分到不能再分。

  不過這邊不需要返回執行結果,只需要對於每個拆分出來的兩個對象繼續進行split操作,當不能進行split了,則merge。

  spilit的實現如下:

 public void split(int[] ins, int a, int b) {//3  6
        int mid = (a + b) / 2;
        if (b - a <= 2) {
            //無非兩種情況
            if (b - 1 > a && ins[a] > ins[b - 1]) {
                int temp = ins[a];
                ins[a] = ins[b - 1];
                ins[b - 1] = temp;
            }
            return;
        }
        split(ins, a, mid);
        split(ins, mid, b);
        merge(ins, a, mid, mid, b);
    }

  split傳入的參數就是待排序數組ins,排序區間[a,b)本身是個遞歸算法,所有操作直接在ins上執行就好了,所以是不需要返回值的(返回值也沒啥意義)。

  其邏輯就是拆分拆分拆分和mergemergemerge。不過需要個if去判斷一下待排序區間的大小,如果區間只有兩個元素,判斷一下然後進行一下交換就行了。至於為毛要有這個一個if,因為if語句後面的語句依然執行的是split,其中依然需要傳入排序區間[a,b),上文也提到了技術分享圖片那這裏的這個if,指的,也是處理的,就是這個過程,因為merge是在split之後訪問的,所以這個判斷過程只能寫到split裏面,而不能搞到merge裏面。

  之後需要對merge方法進行一些改造,大致就是,原來的merge是由兩個int數組創建一個新的int數組,而新的merge方法,最好是接近原地算法,在原數組上直接進行修改,那用數組,跟排序區間,即可表示原來的一個數組。

public void merge(int[] ori, int c1_left, int c1_right, int c2_left, int c2_right) {
        int[] temp = new int[c1_right - c1_left];
        System.arraycopy(ori, c1_left, temp, 0, temp.length);

        int c1_length = c1_right - c1_left;

        for (int i = 0, j = c2_left, index = c1_left; i < c1_length || j < c2_right; ) {
            if (i < c1_length) {
                if (j < c2_right) {
                    //都合法
                    if (temp[i] >= ori[j]) {
                        ori[index++] = ori[j++];
                    } else {
                        ori[index++] = temp[i++];
                    }
                } else {
                    ori[index++] = temp[i++];
                }
            } else {
                ori[index++] = ori[j++];
            }
        }
    }

  不過似乎無論是在split還是在merge當中,由於其大量的尋秩操作,其對秩和位置進行操作,而不是對數組中的值進行操作,流以及函數式編程,在這種算法中的應用範圍不廣,並不適合。

【算法導論 in lambda】並歸排序