歸併(Merge Sort)排序算法

歸併排序(Merge sort)是利用"歸併"技術來進行排序的。歸併是指將若干個已排序的子文件合併成一個有序的文件。

1、歸併排序的基本思想

兩個歸併算法的基本思想:設有兩個有序的子文件(相當於輸入堆)放在同一向量中相鄰的位置上:A[low...m],A[m+1...high],先將他們合併到一個暫存的向量Temp(相當於 輸出堆)中,待合併完成後將Temp複製回A[low...high]中。

歸併排序有兩種實現方法:自底向上和自頂向下。

自底向上方法的基本思想:

(1) 第1趟歸併排序時,將待排序的文件A[1...n]看作n個長度爲1的有序子文件,將這些子文件兩兩歸併。若n爲偶數,則得到n/2個長度爲2的有序子文件;若n爲奇數,則 最後一個子文件不參與歸併。所以本趟歸併完成後,前n/2個有序子文件長度爲2,但最後一個子文件長度爲1。

(2) 第2趟歸併則是將第1趟歸併所得到的n/2個有序的子文件兩兩歸併,如此反覆,直到最後得到一個長度爲n的有序文件爲止。

(3) 上述每次歸併操作,均是將兩個有序的子文件合併成一個有序的子文件,所以稱爲"二路歸併排序"。類似地,有k(k>2)路歸併排序。

自頂向下方法的基本思想更爲簡潔:設歸併排序的當前區間是A[low...high],步驟如下:

(1) 分解:將當前的區間一分爲而,即求分裂點。

(2) 求解:遞歸地對兩個子區間A[low...mid]和A[mid+1...high]進行歸併排序。

(3) 組合:將已排序的兩個子區間A[low...mid]和A[mid+1...high]歸併爲一個有序的區間R[low...high]。

(4) 遞歸的終止條件:子區間長度爲1(一個記錄自然有序)。

2、思想

多次將兩個或兩個以上的有序表合併成一個新的有序表。 

3、算法複雜度

最好的情況下:一趟歸併需要n次,總共需要logN次,因此爲O(N*logN)

最壞的情況下,接近於平均情況下,爲O(N*logN)

說明:對長度爲n的文件,需進行logN 趟二路歸併,每趟歸併的時間爲O(n),故其時間複雜度無論是在最好情況下還是在最壞情況下均是O(nlgn)

4、穩定性

歸併排序最大的特色就是它是一種穩定的排序算法。歸併過程中是不會改變元素的相對位置的。

缺點是,它需要O(n)的額外空間。但是很適合於多鏈表排序。(例如將兩個有序鏈表合併成一個有序的鏈表)

5、代碼實現

//實現歸併排序的算法(使用自頂向下的方法)
//歸併排序是複雜的排序。
//1.在歸併排序之前,先分配一個數組長度大小的臨時空間,用於存放數組歸併排序的結果.
//2.先將整個數組每次二分,二分的終止條件是低位下標大於等於高位,遞歸調用直到終止.
//3.調用歸併時,傳入左側開始位置、右側開始位置和右側結束位置,臨時數組開始位置爲左側位置,左側結束位置爲右側開始位置-1
//4.確定臨時數組長度爲右末-左初+1.
//5.先從左初位置和右初位置開始遍歷,比較兩端的元素值,把較小的值放入temp臨時數組中,再從中找出次小的值存入數組中,直到左初位置大於左末,或者是右初位置大於右末位置。
//6.不管怎麼操作肯定會有一段含有剩餘元素,遍歷左初到左末,右初到右末位置,把剩餘元素放入數組中
//7.把臨時數組中的數據拷貝到原始數組中,循環次數爲數組長度,從右末位置開始。
//8.循環2-8步,直到原始數組有序爲止。
#include<iostream>
using namespace std;

//將分值的兩端按大小次序填入臨時數組,最後臨時數組拷貝到原始數組中
//lPos到rPos-1爲一端,rPos到rEnd爲另外一端
static void Merge(int a[], int temp[], int lPos, int rPos, int rEnd)
{
	int i, NumElement, lEnd, tempPos;
	tempPos = lPos;
	lEnd = rPos - 1;						//從左端開始
	NumElement = rEnd - lPos + 1;					//數組長度

	while ((lPos <= lEnd) && (rPos <= rEnd))
	{
		if (a[lPos] <= a[rPos])					//比較兩端的元素值
			temp[tempPos++] = a[lPos++];			//把較小的值先放入temp臨時數組中
		else
			temp[tempPos++] = a[rPos++];
	}

	//到這裏,左端大或者右端只能有一端還可能含有剩餘元素
	while (lPos <= lEnd)						//把左端剩餘的元素放入temp中
		temp[tempPos++] = a[lPos++];
	while (rPos <= rEnd)						//把右端剩餘的元素放入temp中
		temp[tempPos++] = a[rPos++];
	for (i = 0; i < NumElement; i++, rEnd--)
		a[rEnd] = temp[rEnd];					//把臨時數組拷貝到原始數組中
}

static void msort(int a[], int temp[], int low, int high)
{
	if (low >= high)						//結束條件,當low和high中間沒有元素return
		return;
	int middle = (low + high) / 2;					//計算分裂點
	msort(a, temp, low, middle);					//對子區間[low,middle]遞歸做歸併排序
	msort(a, temp, middle + 1, high);				//對子區間[middle+1,high]遞歸做歸併排序
	Merge(a, temp, low, middle + 1, high);			<span style="white-space:pre">	</span>//組合,把兩個有序區合併爲一個有序區
}
static void merge_sort(int a[], int len)
{
	int* temp = NULL;						//分配臨時數組空間
	temp = new int[len];
	if (temp != NULL)
	{
		msort(a, temp, 0, len - 1);				//調用msort歸併排序
		delete[] temp;						//釋放臨時數組內存
	}
}

static void print_array(int a[], int length)
{
	for (int i = 0; i < length; i++)				//打印元素
	{
		cout << a[i] << " ";
	}
}
void main9mianshiti7()
{
	int a[] = { 8, 6, 1, 3, 5, 2, 7, 4 };
	cout << "before merge sort:";
	print_array(a, 8);
	merge_sort(a, 8);						//歸併排序
	cout << "\nafter merge sort:";
	print_array(a, 8);
	system("pause");
}
6、測試結果

before merge sort::8 6 1 3 5 2 7 4

after merge sort:1 2 3 4 5 6 7 8 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章