一、分治模式
許多有用的演算法在結構上是遞迴的:為了解決乙個給定的問題,演算法一次或多次遞迴地呼叫其自身以解決緊密相關的若干子問題。這些演算法典型地遵循分治法的思想:將原問題分解為幾個規模較小但類似於原問題的子問題。遞迴的求解這些問題,然後再合併這些子問題的解來建立原問題的解。
分治模式在每層遞迴都有三個步驟:
分解原問題為若干子問題,這些子問題是原問題的規模較小的例項。
解決這些子問題,遞迴地求解各子問題。然而,子問題的規模足夠小,則直接求解。
合併這些子問題的解成原問題的解。
歸併排序演算法完全遵循分治模式。
分解:分解待排序的n各元素的序列各成n/2個元素的兩個子列。
解決:使用歸併排序遞迴地排序兩個子串行。
合併:合併兩個已排序的子列以產生已排序的答案。
歸併演算法中合併過程的偽**如下:
merge(a,p,q,r)1 n1 = q - p + 12 n2 = r - q3 let l[1..n1 + 1] and r[1..n2 + 1] be new arrays4 for i = 1 to n15 l[i] = a[p + i -1]6 for j = 1 to n27 r[j] = a[q + j]8 l[n1 + 1] = ∞9 r[n2 + 1] = ∞10 i = 111 j = 112 for k = p to r13 if l[i] <= r[j]14 a[k] = l[i]15 i = i + 116 else 17 a[k] = r[j]18 j = j + 1
第1行計算子陣列a[p..q]的長度你n1。 第2行計算子陣列a[q+1..r]的長度n2。 在第3行,我們建立長度分別為n1+1,n2+1的陣列l和r,每個陣列中額外的位置儲存哨兵。 第4~5行的for迴圈將子陣列a[p..q]複製到l[1..n1]。 第6~7行的for迴圈將子陣列a[q+1..r]複製到r[1..n2]。 第8~9行將哨兵放在陣列l和r的末尾。 第10~18行在下圖中,通過維持以下迴圈不變式,執行r-p+1個基本操作。
在開始第12~17行for迴圈的每次迭代時,子陣列a[p..k-1]按從小到大的順序包含l[1..n1+1]和r[1..n2+1]中的k-p個最小元素。進而l[i]和r[j]是各自所在陣列中未被複製回陣列a的最小元素。
歸併排序證明迴圈不變式成立
初始化:迴圈第一次迭代之前,有k=p,所以子陣列a[p..k-1]為空。這個空的子陣列包含l和r的k-p = 0個最小元素。又因為i=j=1,所以l[i]和r[j]都是各自所在陣列中未被複製回陣列a的最小元素。
保持:為了理解每次迭代都維持迴圈不變式,首先假設l[i]<=r[j]。這時,l[i]是未被複製回陣列a的最小元素,子陣列a[p..k]將包含k-p+1個最小元素。增加k的值和i的值後,為下次迭代重新建立了該迴圈不變式。反之,若l[i]>=r[j],則第17~18行執行適當的操作來維持該迴圈不變式。
終止:終止時k = r +1。根據迴圈不變式,子陣列a[p..k-1]就是a[p..r]且按從小到大的順序包含l[1...n1+1]和r[1..n2+1]中的k-p = r - p +1個最小元素。陣列l和r一起包含n1+n2+2 = r-p+3個元素。除兩個最大的元素以外,其他所有元素都已被複製回陣列a,這兩個最大的元素就是哨兵。
歸併排序偽**
1 merge-srot(a,p,r)2 if p < r3 q = (p + r)/24 merge-sort(a,p,q)5 merge-sort(a,q + 1,r)6 merge(a,p,q,r)
歸併排序例項圖對示例a = <5,2,4,7,1,3,2,6>進行歸併排序
歸併排序的時間複雜度為o(nlgn)。
因為遞迴樹是lgn + 1層,所以總的代價為cnlgn + cn。
二、歸併排序的具體實現過程
#include #include void merge(int intarray, int begin, int mid, int end) for (i = 0; i < n1; i++) l[i] = intarray[begin + i]; for (j = 0; j < n2; j++) r[j] = intarray[mid + 1 + j]; i = j = 0; k = begin; while (i < n1 && j < n2) else } while (i < n1) while (j < n2) /* 程式執行完畢後,在done處,進行資源釋放 */ goto done;error: printf("malloc has benn failed!");done: if (l != null && r != null) }void merge_sort(int intarray, int head, int tail)}int main(void); int i = 0; merge_sort(a, 0, 7); for (i = 0; i < 8; i++) { printf("%d
二分歸併排序 分治演算法與歸併排序
距離上次寫快排演算法的文章已經過去乙個半月了,和本文要提到的歸併排序演算法類似,快排也是分治思想的一種典型應用,如果有不熟悉快速排序的同學可以翻閱我之前寫過的的快速排序演算法的文章。首先為大家介紹一下什麼是分治,分治是將乙個大問題分割成若干個和原來問題形式相同但規模更小的子問題,然後處理這些小問題,...
歸併排序 二分
歸併排序就是將陣列反覆拆分成兩部分,然後分別在這兩部分裡面再反覆拆分,講拆分成的兩部分按順序排好之後再歸併起來,歸併起來之後再反覆交換位置,最終使整個陣列按順序排列。具體操作方法 按從小到大排 拆分成的兩部分依次比較,若前半部分的較小,將其存入陣列tmp中,將前面的下標i 若後面一部分較小,則將後面...
二分歸併排序
對n個不同的數構成的陣列a 1 n 進行排序,其中n 2 k。二分歸併排序對待排序陣列先劃分後歸併,以陣列49,38,65,97,76,13,27,57為例,在二分歸併中,需要進行如下順序 劃分將原問題歸結為規模為n 2的2個子問題 繼續劃分,將原問題歸結為規模為n 4的4個子問題。繼續 當子問題規...