圖解算法系列之歸並排序
阿新 • • 發佈:2018-12-21
功能 復雜 進入 都沒有 urn 分割 alt 圖解 個數 (1)算法描述
對於給定的線性序列,將當前序列不斷的進行分組,當每個分組的數據只有一個元素時,代表這個分組是有序的,那麽向上合並。每一層兩兩合並,合並的過程是,開辟一個新空間,使用兩個指針同時掃描兩個有序的分組,使得較小的元素或者較大的元素先進入新空間。在不斷的比較之後,如果一個分組的元素為空,直接拷貝另一個沒有被用完的元素到新空間。
(2) 圖解算法
歸並排序的過程
合並函數的合並過程
(3) C/C++代碼實現
CustomSort.h
// 歸並排序 void MergeSort(int arr[], int number); // 內部的歸並排序 void __MergeSort(int arr[], int left, int right); // 內部合並 void __Merge(int arr[], int left, int mid, int right);
CustomSort.cpp
/************************************************************ - 功能描述:實現歸並排序 - int arr[]: 待排序的數組 - int number: 待排序數組中的元素 - 返回值:void ************************************************************/ void MergeSort(int arr[], int number) { // 數組為空或者有一個一下的元素直接返回 if(arr == NULL || number <= 1) { return; } // 調用內部排序算法 __MergeSort(arr, 0, number-1); } /************************************************************ - 功能描述:實現歸並排序的內部排序 - int arr[]: 待排序的數組 - int left: 待排序數組的左邊界 - int right: 待排序數組的右邊界 - 返回值:void ************************************************************/ void __MergeSort(int arr[], int left, int right) { // 如果 right <= left 沒有排序的必要性 if(right <= left) { return; } // 計算中間值 // 沒有使用 (right + left) / 2 是為了避免數據太大導致內存溢出 int mid = (right - left) / 2 + left; // 分別排序兩個分組 __MergeSort(arr, left, mid); __MergeSort(arr, mid+1, right); // 排序完成後就合並兩個分組 __Merge(arr, left, mid, right); } /************************************************************ - 功能描述:實現歸並排序的分組合並 - int arr[]: 待排序的數組 - int left: 待合並數組的左邊界 - int right: 待合並數組的右邊界 - int mid: 待合並數組的中間值 - 返回值:void ************************************************************/ void __Merge(int arr[], int left, int mid, int right) { int len = right - left + 1; // 動態創建數組,因為每個分組的大小都不一樣,使用完需要delete[]空間 int *temp = new int[len]; // 用於第一個分組的指針one // 用於第二個分組的指針two // 用於輔助數組的指針i int one = left; int two = mid + 1; int i = 0; // 判斷只要one和two都沒有越界就不斷的進行比較 while(one <= mid && two <= right) { temp[i++] = arr[one] > arr[two] ? arr[one++] : arr[two++]; } // 判斷哪個數組的指針還沒到頭就直接全都拷貝到temp數組 while(one <= mid) { temp[i++] = arr[one++]; } while(two <= right) { temp[i++] = arr[two++]; } // 往回拷貝數組,註意數組的位置 for(int j = 0; j < len; j++) { arr[left + j] = temp[j]; } delete[] temp; }
(4) Java代碼實現
public class MergeSort { // 歸並排序函數 public static void sort(int[] arr) { // 如果數組為null或者是數組中的元素小於2, 沒有排序的意義 if (arr == null || arr.length < 2) { // 直接返回 return; } // 調用排序函數 mergeSort(arr, 0, arr.length - 1); } // 主要邏輯 public static void mergeSort(int[] arr, int l, int r) { // 如果左邊的指針等於右邊指針, 也就是數組不能再分割 if (l == r) { // 就要直接返回 return; } // 數組中間數值的指針位置 int mid = l + ((r - l) >> 1); // 排序中間位置左邊的數組 mergeSort(arr, l, mid); // 排序中間位置右邊的數組 mergeSort(arr, mid + 1, r); // 合並函數 merge(arr, l, mid, r); } // 合並函數 public static void merge(int[] arr, int l, int m, int r) { // 創建輔助數組 int[] help = new int[r - l + 1]; // 輔助數組的指針位置 int i = 0; // 數組1的指針位置 int p1 = l; // 數組2的指針位置 int p2 = m + 1; // 判斷左邊和右邊的數組是否越界 while (p1 <= m && p2 <= r) { // 如果都沒有越界, 向輔助數組添加數據,類似於外排的方式 help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; } // 如果第一個數組的指針還沒到頭, 就要拷貝數組 while (p1 <= m) { help[i++] = arr[p1++]; } // 如果第二個數組的指針還沒到頭, 就要拷貝數組 while (p2 <= r) { help[i++] = arr[p2++]; } // 拷貝排序後數組 for (i = 0; i < help.length; i++) { arr[l + i] = help[i]; } } }
(5) 時間復雜度分析
當函數出現遞歸調用的時候,一個函數A調用了本身,假定是subA,那麽當前的函數A將會壓入系統的棧內,系統將會保存現場(包括函數執行到哪一行代碼,函數當前的調用狀態以及函數中變量的值),進行下一個函數的執行,經過一層層函數的調用,遇到一個返回條件,系統中的棧中保存的狀態將會一個一個的彈出,也就是函數的恢復現場,最後函數調用結束。
只要符合master公式的都可以使用一下方法計算時間復雜度:
master公式:T(N)=a*T(N/b)+O(Nd)
1) log(b, a) > d 復雜度是O(N log(b,a))
2) log(b, a) = d 復雜度是O(Nd * log(2, N))
3) log(b, a) < d 復雜度是O(Nd)
估計時間復雜度:左側部分的規模和右側部分的規模都是N/2,在整體外排的過程中總共劃過N個數,算式為:T(N)=2T(N/2)+O(N)
,代入master公式,復雜度就是O(N*log2N)
,空間復雜度是O(N)
。
圖解算法系列之歸並排序