歸併排序算法(排序詳解)

 歸併排序

基本思想

歸併排序的核心是將兩個有序序列合併爲一個有序序列。因此將兩個有序序列合併成一個有序序列是歸併排序的基礎算法。歸併排序主要分爲遞歸歸併排序與迭代歸併排序:

遞歸歸併排序即將無序的序列S[s:t]分成兩個部分:S[s:m]和S[m+1:t],m=(s+t)/2。

迭代歸併排序即將初始長度爲n的數組看成n個長度爲1的有序子表,然後將相鄰的有序子表兩兩合併,得到[n/2]個長度爲2或1的有序子表(如果n爲奇數,則最後一個有序子表的長度爲1),再兩兩歸併得到[n/4]個有序子表,以此類推,直至得到長爲n的有序序列。

操作方法

 合併方法:

設a[l:h]是一個線性表(序列),將a[l:h]分爲兩個子序列a[l:m],a[m+1:h],其中,a[l:m]和a[m+1:h]分別有序。那麼我們設置兩個指針:x指針指向a[l],y指針指向a[m+1]。

當他們都不指向末尾時,將*x與*y進行比較,將較小的數放到b[]數組(保存數組)裏,同時,將較小的那個子序列指針右移。

當x指向末尾時,把y指向的元素及其之後的元素全部放進b[]即可。

當y指向末尾時,把x指向的元素及其之後的元素全部放進b[]即可。

遞歸歸併:

遞歸歸併排序即將無序的序列S[s:t]分成兩個部分:S[s:m]和S[m+1:t],m=(s+t)/2。

 

迭代歸併:

迭代歸併排序即將初始長度爲n的數組看成n個長度爲1的有序子表,然後將相鄰的有序子表兩兩合併,得到[n/2]個長度爲2或1的有序子表(如果n爲奇數,則最後一個有序子表的長度爲1),再兩兩歸併得到[n/4]個有序子表,以此類推,直至得到長爲n的有序序列。

 

          

 算法實現

private static void Merge(int a[], int b[], int l, int m, int h) {
				int i, j, k;
				i = l; // i 指的是第一個子序列a[1:m]
				j = m + 1; // j 指的是第二個子序列a[m+1:h]
				k = l; // k 指的是最後用來存放合併後的數組的數組
				while (i <= m && j <= h) {
					if (a[i] < a[j]) {
						b[k] = a[i];
						i++;
						k++;
					} else {
						b[k] = a[j];
						j++;
						k++;
					}
				}
				while (i <= m) {
					b[k++] = a[i++];
				}
				while (j <= h) {
					b[k++] = a[j++];
				}
			}

			// 迭代歸併算法
			private static void mergePass(int S[], int T[], int n, int len) {
				int i = 0;
				while (i + 2 * len < n) {
					Merge(S, T, i, i + len - 1, i + 2 * len - 1);
					i += 2 * len;
				}
				if (i + len <= n) // 一個歸併段長len,另一個對並段長度不足len
				{
					Merge(S, T, i, i + len - 1, n - 1);
				}
			}

			// 迭代實現的歸併排序算法
			public static void mergeSort(int R[], int T[], int n) {
				int temp[] = R.clone();
				int len = 1;
				while (len < n) {
					mergePass(temp, T, n, len);
					temp = T.clone(); // 不加這句的話,temp的數組一直沒有發生變化
					len *= 2;
				}
	}


 

 效率分析

算法性能:


時間複雜度:

歸併排序的形式就是一棵二叉樹,它需要遍歷的次數就是二叉樹的深度,而根據完全二叉樹的可以得出它的時間複雜度是O(n*log2n)。

空間複雜度

由前面的算法說明可知,算法處理過程中,需要一個大小爲n的臨時存儲空間用以保存合併序列。

算法穩定性

在歸併排序中,相等的元素的順序不會改變,所以它是穩定的。

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