在我們之前的算法設計課程中,我們學習了合並排序與自底向上合並排序算法,今天我們就來分析一下這個算法
合並算法
無論是合並排序還是自底向上合並排序,他的實現都基於一個重要的算法:合並算法(merge算法)。Merge算法實現的功能是將兩個數組合並成為一個數組,我們可以定義一個結果數組b,再分別定義兩個索引p1、p2,分別指向兩個數組的頭,將p1、p2指向的兩個數進行比較,並將較小的那一個放入數組b
Merge算法的偽碼如下:
merge(p, q, r, a[]):
- s←p; t←q+1; k←p
- while s≤q and t≤r
- if a[s] ≤ a[t] then
- b[k] ← a[s]
- s ←s+1
- 6. else
- b[k]←a[t]
- t←t+1
- 9. end if
- k←k+1
- 11. end while
- if s=q+1 then b[k…r]←a[t…r]
- else b[k…r]←a[s…q]
- 14. end if
- a[p…r]←b[p…r]
自底向上合並排序算法
自底向上合並算法顧名思義,就是從最底部開始,將數組元素兩兩合並,再將合並后的數組兩兩合並,直到重新合並成一個完整的數組。對於數組a = {9,4,5,2,1,7,4,6}的合並過程可以用如下圖來表示:
自底向上合並排序算法偽碼如下:
BottomSort(a[]):
- t←1
- while t<n
- s←t; t←2s; i←0
- while i+t≤n
- merge(i+1, i+s, i+t, a)
- i←i+t
- 7. end while
- if i+s<n then merge(i+1, i+s, n)
- 9. end while
合並排序算法
合並排序算法與自底向上合並排序十分相似(廢話!),只不過與自底向上合並排序不同的是,合並排序是一個自頂向下的過程。我們可以將一整個數組拆分成兩個數組,將其中的一個數組再進行拆分,直到拆分成兩個單個元素,再將他們合並…重復此過程直到整個數組都重新合並完畢。
對於數組a[]={9,4,5,2,1,7,4,6}的合並排序可以用如下圖示表示:
其中頂端數組表示其獲得的輸入,底端數組表示其合並后的輸出
合並排序算法偽碼如下:
mergesort(low, high, a[]):
- if low<high then
- mid←(low + high)/2
- mergesort(low, mid, a)
- mergesort(mid+1, high, a)
- merge(low, mid, high, a)
- 6. end if
兩個算法的完整代碼
自底向上合並排序(java):
1 //自底向上合並排序 2 public class BottomUpSort { 3 public static void main(String args[]) 4 { 5 int a[] = {9,4,5,2,1,7,4,6}; 6 bottomUpSort(a); 7 for(int i=0;i<a.length;i++) 8 System.out.print(a[i] + " "); 9 System.out.println(); 10 } 11 12 public static void bottomUpSort(int a[]) 13 { 14 int i, s, t = 1; 15 while(t < a.length-1) 16 { 17 s = t; 18 t = 2*s; 19 i = 0; 20 for(;i+t < a.length;i+=t) 21 merge(i, i+s-1, i+t-1, a); //自底向上進行排序 22 if(i+s-1 < a.length-1) //判斷是否有落單元素 23 merge(i, i+s-1, a.length-1, a); 24 25 } 26 } 27 28 public static void merge(int low, int mid, int high, int a[]) 29 { 30 int b[] = new int[a.length]; // 建立轉存數組 31 int f = low, s = mid+1, p = low; 32 //f為第一個數組索引,s為第二個數組索引,p為b數組索引 33 while(f<=mid && s<=high) 34 { 35 //在兩個數組元素中值小的一方放入b數組 36 if(a[f] <= a[s]) 37 { 38 b[p] = a[f]; 39 f++; 40 } 41 else 42 { 43 b[p] = a[s]; 44 s++; 45 } 46 p++; 47 } 48 49 if(f == mid+1) //若第一個數組中的元素全部存儲進去了,那么將第二個數組中的剩余元素全部放入b數組 50 for(;s <= high && p<=high;p++,s++) 51 b[p] = a[s]; 52 else //否則將第一個數組中的元素全部放入b數組 53 for(;f<=mid && p<=high;p++,f++) 54 b[p] = a[f]; 55 56 for(int i=low;i<=high;i++) 57 a[i] = b[i]; 58 } 59 }
自底向上合並排序(c):
1 #include<stdio.h> 2 3 //數組合並函數 4 void merge(int low, int mid, int high, int a[]) 5 { 6 int b[50]; //定義轉存數組 7 int f = low, s = mid + 1, p = low; 8 //f表示第一個數組的索引,s表示第二個數組的索引,p表示b數組的索引 9 10 while (f <= mid && s <= high) 11 { 12 //將兩個數組中值小的一方存入數組b 13 if (a[f] <= a[s]) 14 { 15 b[p] = a[f]; 16 f++; 17 } 18 else 19 { 20 b[p] = a[s]; 21 s++; 22 } 23 p++; 24 } 25 if (f == mid + 1) //若第一個數組都存進了數組b,那么將第二個數組中的剩下元素全部存入 26 for (; s <= high; p++, s++) 27 b[p] = a[s]; 28 else //否則將第一個數組中的剩下元素全部存入 29 for (; f <= mid; p++, f++) 30 b[p] = a[f]; 31 32 for (int i = low; i <= high; i++) 33 a[i] = b[i]; //將合並后的數組轉存回a 34 } 35 36 void BottomUpSort(int low, int high, int a[]) 37 { 38 int t = 1, s, i; 39 int length = high + 1; 40 while (t < length-1) 41 { 42 s = t; 43 t = 2 * s; 44 i = 0; 45 for (; i + t <= length - 1; i+=t) //自底向上進行合並 46 merge(i, i + s - 1, i + t - 1, a); 47 if (i + s-1 < length - 1) //判斷是否有落單元素 48 merge(i, i + s - 1, length - 1, a); 49 } 50 } 51 52 int main() 53 { 54 int a[] = { 9,4,5,2,1,7,4,6 }; 55 int length = sizeof(a) / sizeof(a[0]); 56 for (int i = 0; i < length; i++) 57 printf("%d ", a[i]); 58 printf("\n"); 59 BottomUpSort(0, length - 1, a); 60 for (int i = 0; i < length; i++) 61 printf("%d ", a[i]); 62 printf("\n"); 63 return 0; 64 }
合並排序(java):
1 //合並排序算法 2 public class MergeSort { 3 public static void main(String args[]) 4 { 5 int a[] = {9,4,5,2,1,7,4,6}; 6 mergeSort(0, a.length-1, a); 7 for(int i=0;i<a.length;i++) 8 System.out.print(a[i] + " "); 9 System.out.println(); 10 } 11 12 //合並排序 13 public static void mergeSort(int low, int high, int a[]) 14 { 15 if(low < high) 16 { 17 int mid = (low + high)/2; 18 mergeSort(low, mid, a); 19 mergeSort(mid+1, high, a); 20 merge(low, mid, high, a); 21 } 22 } 23 24 //合並兩個數組 25 public static void merge(int low, int mid, int high, int a[]) 26 { 27 int b[] = new int[a.length]; // 建立轉存數組 28 int f = low, s = mid+1, p = low; 29 //f為第一個數組索引,s為第二個數組索引,p為b數組索引 30 while(f<=mid && s<=high) 31 { 32 //在兩個數組元素中值小的一方放入b數組 33 if(a[f] <= a[s]) 34 { 35 b[p] = a[f]; 36 f++; 37 } 38 else 39 { 40 b[p] = a[s]; 41 s++; 42 } 43 p++; 44 } 45 46 if(f == mid+1) //若第一個數組中的元素全部存儲進去了,那么將第二個數組中的剩余元素全部放入b數組 47 for(;s <= high && p<=high;p++,s++) 48 b[p] = a[s]; 49 else //否則將第一個數組中的元素全部放入b數組 50 for(;f<=mid && p<=high;p++,f++) 51 b[p] = a[f]; 52 53 for(int i=low;i<=high;i++) 54 a[i] = b[i]; 55 } 56 }
合並排序(c):
1 #include<stdio.h> 2 3 //數組合並函數 4 void merge(int low, int mid, int high, int a[]) 5 { 6 int b[50]; //定義轉存數組 7 int f = low, s = mid + 1, p = low; 8 //f表示第一個數組的索引,s表示第二個數組的索引,p表示b數組的索引 9 10 while (f <= mid && s <= high) 11 { 12 //將兩個數組中值小的一方存入數組b 13 if (a[f] <= a[s]) 14 { 15 b[p] = a[f]; 16 f++; 17 } 18 else 19 { 20 b[p] = a[s]; 21 s++; 22 } 23 p++; 24 } 25 if (f == mid+1) //若第一個數組都存進了數組b,那么將第二個數組中的剩下元素全部存入 26 for (; s <= high; p++, s++) 27 b[p] = a[s]; 28 else //否則將第一個數組中的剩下元素全部存入 29 for (; f <= mid; p++, f++) 30 b[p] = a[f]; 31 32 for (int i = low; i <= high; i++) 33 a[i] = b[i]; //將合並后的數組轉存回a 34 } 35 36 //合並排序函數 37 void MergeSort(int low, int high, int a[]) 38 { 39 if (low < high) 40 { 41 int mid = (low + high) / 2; 42 MergeSort(low, mid, a); 43 MergeSort(mid + 1, high, a); //從上到下合並數組 44 merge(low, mid, high, a); 45 } 46 } 47 48 int main() 49 { 50 int a[] = { 9,4,5,2,1,7,4,6 }; 51 int length = sizeof(a) / sizeof(a[0]); 52 for (int i = 0; i < length; i++) 53 printf("%d ", a[i]); 54 printf("\n"); 55 MergeSort(0, length - 1, a); 56 for (int i = 0; i < length; i++) 57 printf("%d ", a[i]); 58 printf("\n"); 59 return 0; 60 }