【算法導論 in lambda】並歸排序
並歸排序的過程就是一個先拆再合並的過程,先拆到全是不能再拆的最小數組,然後組與組之間合並,排序的過程在合並的過程中執行。
所以整個算法分兩部分,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】並歸排序