1. 程式人生 > >【排序演算法】歸併排序(C++實現)

【排序演算法】歸併排序(C++實現)

歸併排序是利用"歸併"技術來進行排序。歸併是指將若干個已排序的子檔案合併成一個有序的檔案。常見的歸併排序有兩路歸併排序(Merge Sort),多相歸併排序(Polyphase Merge Sort),Strand排序(Strand Sort)。下面介紹第一種:

(一)兩路歸併排序

最差時間複雜度:O(nlogn)
平均時間複雜度:O(nlogn)
最差空間複雜度:O(n)
穩定性:穩定

兩路歸併排序(Merge Sort),也就是我們常說的歸併排序,也叫合併排序。它是建立在歸併操作上的一種有效的排序演算法,歸併操作即將兩個已經排序的序列合併成一個序列的操作。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。
歸併操作的基本步驟如下:
1.申請兩個與已經排序序列相同大小的空間,並將兩個序列拷貝其中;
2.設定最初位置分別為兩個已經拷貝排序序列的起始位置,比較兩個序列元素的大小,依次選擇相對小的元素放到原始序列;
3.重複2直到某一拷貝序列全部放入原始序列,將另一個序列剩下的所有元素直接複製到原始序列尾。

設歸併排序的當前區間是R[low..high],分治法的三個步驟是:
1.分解:將當前區間一分為二,即求分裂點
2.求解:遞迴地對兩個子區間R[low..mid]和R[mid+1..high]進行歸併排序;
3.組合:將已排序的兩個子區間R[low..mid]和R[mid+1..high]歸併為一個有序的區間R[low..high]。
遞迴的終結條件:子區間長度為1(一個記錄自然有序)。

演算法示意圖:


程式碼實現:

void Merge(int *a, int p, int q, int r)
{
	int n1 = q-p+1;
	int n2 = r-q;
	int *L = new int[n1+1];
	int *R = new int[n2+1];
	int i, j, k;
	
	for (i=0; i<n1; i++){
		L[i] = a[p+i];
	}
	for (j=0; j<n2; j++){
		R[j] = a[q+j+1];
	}
	L[n1] = 10000000;
	R[n2] = 10000000;

	for (i=0, j=0, k=p; k<=r; k++)
	{
		if (L[i]<=R[j])
		{
			a[k] = L[i];
			i++;
		}else{
			a[k] = R[j];
			j++;
		}
	}

	delete []L;
	delete []R;
}

void MergeSort1(int *a, int p, int r)
{
	if (p<r)
	{
		int q = (p+r)/2;
		MergeSort1(a, p, q);
		MergeSort1(a, q+1, r);
		Merge(a, p, q, r);
	}
}

雖然插入排序的時間複雜度為O(n^2),歸併排序的時間複雜度為O(nlogn),但插入排序中的常數因子使得它在n較小時,執行得要更快一些。因此,在歸併排序演算法中,當子問題足夠小時,採用插入排序演算法就比較合適了。

程式碼實現:

void MergeSort2(int *a, int p, int r)
{
	if ((r-p)>=50) // 小於50個數據的陣列進行插入排序
	{
		int q = (p+r)/2;
		MergeSort2(a, p, q);
		MergeSort2(a, q+1, r);
		Merge(a, p, q, r);
	}else
	{
		InsertionSort(a+p, r-p+1);
	}
}

MergeSort1與MergeSort2演算法排序時間實驗結果比較:

資料量

1K

10K

100K

1000K

10000K

MergeSort1

0.001s

0.008s

0.065s

0.552s

5.875s

MergeSort2

<0.001s

0.001s

0.021s

0.219s

2.317s