排序演算法——歸併排序
什麼是歸併排序?
歸併排序簡單來講,就是將兩個有序的序列整合到一起。
如何將兩個有序的序列整合到一起呢?
那麼我們假設,現在有 M={m1 ,m2,m3,....,mx}序列和 N = {n1,n2,n3,....,ny}序列,這兩個序列已經是有序的序列,首先建立一個空序列 K = {},那麼接著將 m1 和 n1 進行比較,加入 m1 < n1 那麼將 m1 放入 K 序列中,然後 M 序列遊標後移,即下一次將進行 m2 和 n1 的比較,直到全部比較完畢,並填入序列 K 中。
既然歸併排序是整合有序序列,那麼豈不是不能排序無序序列,這條件也太苛刻了點吧?
歸併排序是建立在分治法的基礎上進行操作的,也就是可以將一個完全無序的序列進行無線分割以達到有序序列,比如:現在有 M={m1 ,m2,m3,....,mx},M序列是完全無序的序列,那麼此時可以將 M 序列分成若干個小序列——{m1,m2},{m3,m4}....{mx-1,mx};此時這些序列就是有序的,那麼就可以通過歸併操作進行排序,所以歸併排序也可以排序無序序列,且速度僅次於快速排序,屬於穩定排序。
如何分割序列?
上個問題已經提過,歸併排序是建立在分治的基礎上進行操作的,其中分治的體現就體現在分割序列上,比如:現在有M={m1 ,m2,m3
該演算法具體步驟:
1、規定首指標 left 和mid(left指向序列1的首元素,right 指向序列2的首元素)
2、比較 left 和 mid 的大小,將小的元素放入新建的空間中
3、重複步驟2,直到其中一個序列遍歷完畢
4、將剩下的未加入新建空間中的元素直接複製貼上進新建空間
該演算法的核心步驟:
1、使用分治法(遞迴)分割序列
2、比較 left 和 mid 的大小 , 將小的元素的新增進入新建空間
講述完畢,貼上程式碼:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 排序__歸併排序 8 { 9 class 歸併 10 { 11 12 public static int[] arr = { 6, 202, 301, 100, 38, 8, 1 ,-1,1000}; 13 static void Main(string[] args) 14 { 15 Sort(arr, 0, arr.Length -1); 16 foreach (var item in arr) 17 { 18 Console.Write(item + " "); 19 } 20 Console.ReadKey(); 21 } 22 23 public static void Sort(int []a,int left,int right) 24 { 25 if (left >= right) return; 26 else 27 { 28 int mid = (left + right) / 2; //@1 29 Sort(a, left, mid); 30 Sort(a, mid + 1, right); 31 MergeSort(a, left, mid, right); 32 } 33 } 34 35 public static void MergeSort(int []a,int left,int mid,int right) 36 { 37 int[] Arr = new int[right - left + 1]; 38 int l = left, m = mid +1 , k = 0; //@2 39 while ( m <= right && l <= mid ) //@3 40 { 41 if (a[l] > a[m]) Arr[k++] = a[m++]; 42 else Arr[k++] = a[l++]; 43 } 44 while (m < right +1) //@4 45 { 46 Arr[k++] = a[m++]; 47 } 48 while (l < mid +1 ) Arr[k++] = a[l++]; //@4 49 for (k = 0, l = left; l < right + 1; k++, l++) a[l] = Arr[k]; 50 } 51 } 52 }View Code
程式碼解讀:
@1 :Sort()函式完成了序列的分割,每一次遞迴都將序列分成兩半,直到不能分隔為止。
@2 :l 代表序列1的首元素,m 代表序列2的首元素,k代表新建陣列Arr的已有元素個數
@3 :序列1的首元素和序列2的首元素進行比較,將小的放入 Arr 中,且序列遊標後移,直到其中一個序列的元素遍比較完畢
@4 :在序列1 和序列2的比較過程中,可能出現序列1已經全部新增進了 Arr 中,但是序列2還沒有,則將剩下的未比較的元素直接複製進入 Arr 中
總結:
以上程式碼並不是真正意義上的分割陣列,只是做了一個邏輯分割,沒有像其他程式碼一樣將分割後的陣列填入一個新的陣列,那樣做的話會對速度和記憶體產生一點影響,雖然微乎其微,但是總歸是沒這麼好的,在歸併操作上,需要注意的是引數不要越界(我當時就想了很久為什麼 @2 上面的 m 要等於 mid +1 而不是 mid ,其實道理很簡單,因為mid是屬於左序列,從 mid+1 開始才屬於右序列,將m = mid +1 改成 m = mid 不是不行,只是後面的程式碼也需要改,但是沒有必要多做一次無用比較,這個問題我當時真是想了半天...),其實只要理清楚其中的邏輯關係,這樣程式碼就能做到信手拈來。
&n