歸並排序是典型分治思想的代表——首先把原問題分解為兩個或多個子問題,然后求解子問題的解,最后使用子問題的解來構造出原問題的解。
對於歸並排序,給定一個待排序的數組,首先把該數組划分為兩個子數組,然后對子數組進行排序(遞歸調用歸並排序),最后對兩個有序的子數組進行合並,使合並之后的數組為有序狀態。
讓我們想想,把一個數組不斷地划分為子數組,不斷地划分......,不斷地划分......., 最后停止了划分不下去了。 此時子數組的元素有一個,它們本身就是有序的。接下來,我們就需要執行合並過程,不斷地一層層向上合並,........, 直到原數組。通過這個過程就會發現, 歸並排序的核心在於合並有序的子數組,而不是對子數組進行排序,因為最底層的子數組本身就是有序的,上一層子數組如果想要變成有序的,通過合並底層有序的子數組就可以得到, 最終我們使原數組變成了有序的,從而完成了排序操作。
說明幾點:
1. 歸並排序采用了分治思想,它的核心在於合並子問題的解而不是求解子問題(快速排序也采用了分治思想,但它的核心是在於求解子問題而不需要合並子問題的解)、
2. 歸並排序不是原址排序,它有排序過程中需要借助額外的內存空間。
3. 歸並排序為穩定排序(其實呢,具體還得看你怎么寫代碼,如果兩個數的值相等時,你不保持原順序都就會變成非穩定的了)
4. 歸並排序的時間復雜度為O(NlogN).
具體代碼如下:
1 /*********************************************************************** 2 * Copyright (C) 2019 Yinheyi. <chinayinheyi@163.com> 3 * 4 * This program is free software; you can redistribute it and/or modify it under the terms 5 * of the GNU General Public License as published by the Free Software Foundation; either 6 * version 2 of the License, or (at your option) any later version. 7 8 * Brief: 9 * Author: yinheyi 10 * Email: chinayinheyi@163.com 11 * Version: 1.0 12 * Created Time: 2019年05月06日 星期一 22時22分57秒 13 * Modifed Time: 2019年05月09日 星期四 21時10分59秒 14 * Blog: http://www.cnblogs.com/yinheyi 15 * Github: https://github.com/yinheyi 16 * 17 ***********************************************************************/ 18 19 20 // 歸並排序,分治法的典型代表: 將原問題分解了幾個子問題,解決子問題,再合並子問題的解, 21 // 這樣就得到了原問題的解。 22 // 分治本質上就是把原問題分解為幾個子問題來解決。 23 // 快速排序也是分治思想來解決。 24 // 25 // 26 // 歸並排序(merge-sort): 27 // 1. 把一個待排序的數組分解為兩個子數組; 28 // 2. 對兩個子數組進行排序(通過遞歸地調用自己來實現); 29 // 3. 對兩個已經排序的數組進行合並。 30 // 31 // 分析: 32 // 1. 一個數組一直分解下去,只到分解成只包含一個元素的子數組為止, 此時自然是有序的; 33 // 2. 歸並排序的重點在於合並,而不是對子數組的排序。(快速排序與它恰恰相反,快速排序的 34 // 重點是對子數組進行排序,而不是合並,因為它不需要合並了) 35 // 36 // 37 #include <cstring> 38 #include <iostream> 39 typedef bool(*CompareFunc)(int, int); 40 41 // 下面函數實現合並功能,輸入三個下標參數表示了兩個子數組, :[nStart_, nMiddle)和[nMiddle, nEnd) 42 void Merge(int array[], int nStart_, int nMiddle_, int nEnd_, CompareFunc comp) 43 { 44 if (array == nullptr || nStart_ >= nMiddle_ || nMiddle_ >= nEnd_) 45 return; 46 47 // 建立一個臨時數組存放中間數據 48 int _nIndex = 0; 49 int* _pTempArray = new int[nEnd_ - nStart_]; 50 51 // 對兩個子數組進行合並 52 int _nStartChange = nStart_; 53 int _nMiddleChange = nMiddle_; 54 while (_nStartChange < nMiddle_ && _nMiddleChange < nEnd_) 55 { 56 // 此處的if中比較語句的安排可以保持穩定排序的特性。 57 if (comp(array[_nMiddleChange], array[_nStartChange])) 58 { 59 _pTempArray[_nIndex] = array[_nMiddleChange]; 60 ++_nMiddleChange; 61 } 62 else 63 { 64 _pTempArray[_nIndex] = array[_nStartChange]; 65 ++_nStartChange; 66 } 67 ++_nIndex; 68 } 69 70 // 把不為空的子數組的元素追加到臨時數 71 if (_nStartChange < nMiddle_) 72 { 73 memcpy(_pTempArray + _nIndex, array + _nStartChange, sizeof(int) * (nMiddle_ - _nStartChange)); 74 } 75 else if (_nMiddleChange < nEnd_) 76 { 77 memcpy(_pTempArray + _nIndex, array + _nMiddleChange, sizeof(int) * (nEnd_ - _nMiddleChange)); 78 } 79 else 80 { 81 /* do noting */ 82 } 83 84 // 數據交換 85 memcpy(array + nStart_, _pTempArray, sizeof(int) * (nEnd_ - nStart_)); 86 87 delete [] _pTempArray; 88 _pTempArray = nullptr; 89 } 90 91 // 歸並排序功能實現函數 92 void MergeSort(int array[], int nStart_, int nEnd_, CompareFunc comp) 93 { 94 // 數組指針為空,或者數組內的個數少於等於1個時,直接返回。 95 if (nullptr == array || (nEnd_ - nStart_) <= 1) 96 return; 97 98 // 划分為兩個子數組並遞歸調用自身進行排序 99 int _nMiddle = (nStart_ + nEnd_) / 2; 100 MergeSort(array, nStart_, _nMiddle, comp); 101 MergeSort(array, _nMiddle, nEnd_, comp); 102 103 // 合並排序完成的子數組 104 Merge(array, nStart_, _nMiddle, nEnd_, comp); 105 } 106 107 // 比較函數 108 bool less(int lhs, int rhs) 109 { 110 return lhs < rhs; 111 } 112 113 // 打印數組函數 114 void PrintArray(int array[], int nLength_) 115 { 116 if (nullptr == array || nLength_ <= 0) 117 return; 118 119 for (int i = 0; i < nLength_; ++i) 120 { 121 std::cout << array[i] << " "; 122 } 123 124 std::cout << std::endl; 125 } 126 127 /*************** main.c *********************/ 128 >>int main(int argc, char* argv[]) 129 { 130 // 測試1 131 int array[10] = {1, -1, 1, 231321, -12321, -1, -1, 123, -213, -13}; 132 PrintArray(array, 10); 133 MergeSort(array, 0, 10, less); 134 PrintArray(array, 10); 135 136 // 測試2 137 int array2[1] = {1}; 138 PrintArray(array2, 1); 139 MergeSort(array2, 0, 1, less); 140 PrintArray(array2, 1); 141 142 // 測試3 143 int array3[2] = {1, -1}; 144 PrintArray(array3, 2); 145 MergeSort(array3, 0, 2, less); 146 PrintArray(array3, 2); 147 148 return 0; 149 }