歸併排序【Java】
程式碼:
1 public static void mergeSort(int[] arr) {
2 if (arr == null || arr.length < 2) {
3 return;
4 }
5 mergeSort(arr, 0, arr.length - 1);
6 }
7
8 public static void mergeSort(int[] arr, int l, int r) {
9 if (l == r) {
10 return ;
11 }
12 int mid = l + ((r - l) >> 1);
13 mergeSort(arr, l, mid); //左側有序
14 mergeSort(arr, mid + 1, r); //右側有序
15 merge(arr, l, mid, r); //merge
16 }
17
18 public static void merge(int[] arr, int l, int m, int r) {
19 int[] help = new int[r - l + 1];
20 int i = 0;
21 int p1 = l;
22 int p2 = m + 1;
23 while (p1 <= m && p2 <= r) {
24 help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; //誰小拷貝誰(p1和p2都不越界)
25 }
26 while (p1 <= m) {
27 help[i++] = arr[p1++];
28 }
29 while (p2 <= r) {
30 help[i++] = arr[p2++];
31 }
32 for (i = 0; i < help.length; i++) {
33 arr[l + i] = help[i];
34 }
35 }
如果看程式碼的流程,歸併排序的是把一組資料,一分為二,之後對左邊的資料繼續一分為二,分到實在不能再分(也就是l == r的時候)
最後再排序!這其實是分而治之的思想,假設有5個數【2,4,9,6,3】我們直接排序不好排,但是如果我們分一下呢?
變成【2,4】和【9,6,3】如果對左邊再分一次呢?
【2】、【4】這時候2 和 4 都是單獨的一個數,我們顯然知道單獨的一個數是最好排序的,2 和4 本身就是有序的,因為它們只有自己!
對於左邊,當分到不能再分的時候return;,遞迴呼叫的“遞”的過程就完成了,接下來就是歸了。
在歸的過程中,會多次進入右側的遞迴函式,但是由於 l == r 所以實質上右側的呼叫是無效的,只需要呼叫merge函式完成左側單個數據
的依次排序!
當左側完成的時候,會進入最初始的那個 l 和 r 的值,接下來再次進入右側的遞迴函式,完成同樣的一個過程,最後左側資料和右側資料
都是各自有序的,當程式跳出(mergeSort(arr, mid + 1, r); //右側有序)這一行程式碼的時候,會執行最外層函式的最後一次merge,這時候
對所有資料完成最後一次排序引數應該是最初的引數,也就是 l = 0 ; mid = 2 ; r = 4 ! merge(0,2.4) 這時候所有資料就都是有序的了!
程式碼的執行順序?
當進入一條遞迴語句的時候,會多次迴圈整個過程,只有當滿足結束條件的時候,才會return!
但此時要注意,這時候return只是退出了這一條語句而已,對於最外層的函式來說,只是這一條
語句執行完畢了。還是需要繼續往下執行的,也就是會進入下一條遞迴語句,對於歸併排序來說
先進入左邊遞迴,左邊有序,之後進入右邊遞迴,右邊有序!最後進入最外層的函式執行最後一
條merge語句,把整個陣列完成排序!
下面是我畫的一個草圖:
該圖片來自:https://www.cnblogs.com/l199616j/p/10604351.html