1. 程式人生 > >C++實現歸併排序Mergesort(使用遞迴的方法)

C++實現歸併排序Mergesort(使用遞迴的方法)

歸併排序的最壞執行時間為O(NlogN)。它的思想是把兩個有序的子序列,通過一次遍歷,合併且有序的放在第三個序列中。顯然合併兩個已排序的表的時間是線性的,最多進行N1次比較,其中N是元素的總個數。對總的執行時間進行推導如下:
假設N是2的冪次,從而每次總能分裂成兩個相等的子列。對於N=1的子列,歸併排序的時間為常數,我們記為1,那麼我們可以得到如下的遞推關係(參考Weiss的《資料結構與演算法分析》第三版C++語言描述):

T(1)T(N)=1=2T(N/2)+N
其中,對於N個數的歸併排序用的時間等於兩個大小為N/2的歸併排序所用的時間加上合併的時間。
把上面的式子兩邊同時除以N,得到以下的遞推關係:
T
(N)
N
T(N/2)N/2T(N/4)N/4T(2)2
=2T(N/2)N/2+1=T(N/4)N/4+1=T(N/8)N/8+1=T(1)1+1

然後把上面的式子相加,消去等號兩邊相等的項,該過程稱之為疊縮求和,最後得到的結果為
T(N)N=T(1)1+logN
因為總的方程個數等於執行歸併排序中子列長度變化的次數k=logN2
所以得到:
T(N)=NT(1)+NlogN
得證。

下面給出使用遞迴方法的程式:


#include<iostream>
#include<vector>
#include<random>
#include<ctime>
#include<iterator> #include<algorithm> using namespace std; /* 使用遞迴的歸併排序 該函式為驅動函式 */ template<typename Comparable> void mergeSortRecursive(vector<Comparable> &a) { vector<Comparable> tmpArray(a.size()); mergeSortRecursive(a, tmpArray, 0, a.size() - 1); } /* 實現採用遞迴的歸併排序 a為原始資料 tmpArray用於存放歸併排序過程中的結果 left表示迭代中子列的最左端的下標 right表示迭代中子列的最右端的下標 */
template<typename Comparable> void mergeSortRecursive(vector<Comparable> &a, vector<Comparable> &tmpArray, int left, int right) { if (left < right) { int center = (right + left) / 2; mergeSortRecursive(a, tmpArray, left, center); mergeSortRecursive(a, tmpArray, center + 1, right); merge(a, tmpArray, left, center + 1, right); } } /* 歸併排序中使用的合併函式 a為原始資料 tmpArray用於存放歸併排序過程中的結果 leftPos表示前一個子列的最左端的元素的下標 rightPos表示後一個子列的最左端的元素的下標 rightEnd表示後一個子列的最右端的元素的下標 */ template<typename Comparable> void merge(vector<Comparable> &a, vector<Comparable> &tmpArray, int leftPos, int rightPos, int rightEnd) { int leftEnd = rightPos - 1; int tmpPos = leftPos; // 用來儲存在合併過程中存放結果的臨時向量的下標 int numElements = rightEnd - leftPos + 1; //主迴圈,把資料合併 while (leftPos <= leftEnd && rightPos <= rightEnd) { if (a[leftPos] < a[rightPos]) tmpArray[tmpPos++] = a[leftPos++]; else tmpArray[tmpPos++] = a[rightPos++]; } //如果是因為後邊子列的資料全部放在臨時向量中導致主迴圈結束 //則把前面沒放完的資料依次放入臨時變數中 while (leftPos <= leftEnd) tmpArray[tmpPos++] = a[leftPos++]; //同上處理前面子列資料全部先放入向量中的情況 while (rightPos <= rightEnd) tmpArray[tmpPos++] = a[rightPos++]; //注意!不能直接用a=tmpArray,因為可能只是複製子列 for (int i = 0; i < numElements; ++i, --rightEnd) a[rightEnd] = tmpArray[rightEnd]; } /*輸出向量*/ template<typename T> void printVector(vector<T> & v) { copy(v.cbegin(), v.cend(), ostream_iterator<T>(cout, " ")); cout << endl; } int main() { vector<int> source; uniform_int_distribution<int> u(0, 1000); default_random_engine e(static_cast<unsigned int>(time(0))); for (int i = 0; i < 31; i++) { source.push_back(u(e)); } cout << "排序前:" << endl; printVector(source); mergeSortRecursive(source); cout << "排序後:" << endl; printVector(source); return 0; }

該程式對merge每次都呼叫相同的臨時向量,因為每次呼叫merge的時候只有一個臨時向量在使用,因此建立和待排向量等長的臨時向量,每次使用的地方都是正在執行歸併排序操作下標起始處。

執行結果如下:
這裡寫圖片描述