算法導論學習筆記(2)-歸並排序
阿新 • • 發佈:2017-06-24
mar 今天 iostream 介紹 font 額外 遞歸 size dsm
今天學習了算法導論上的歸並排序算法,而且完畢了在紙上寫出偽代碼,曾經就學過歸並可是理解的不夠透徹。以
前還一直困惑:為什麽明明歸並排序比快排的時間復雜度更穩定。為什麽庫函數不用歸並而用快排。如今知道原因了,由於歸並排序必須開額外的空間。並且空間開銷還比較大,以下介紹算法:
首先,歸並排序用到了分治的思想。把大數據分成若幹個小數據,然後再分別對小數據進行處理,最後把小數據
合並成大數據。
其次。歸並排序用到了一個最重要的特點。就是把兩組已經排序的數據合並成一組有序數據,而且該過程的時間復
雜度為O(n)。
有序序列,那麽。我們就能夠在O(n)的時間內排好序,但是。問題是,A[0~n/2]和A[n/2+1,n-1]並非有序序列,於是我們就要將他們都變成有序序列,怎樣變呢?我們再分別對A[0~n/2]和A[n/2+1,n-1]進行排序就可以,對於A[0~n/2]來說,我們運用和以上同樣的方法,把他分成A[0~n/2/2]和A[n/2+1,n/2],然後假設這兩個數組均為有序序列的話,那麽就能夠把它們合並起來,然後在返回到上一層了。那麽怎樣才幹推斷他們為有序呢?當僅僅有一個元素的時候這個元素便是有序的。所以,僅僅須要遞歸到元素個數為1。然後再返回合並就可以。最後,算法便出來了,對於一個數組A[n]來說,我們要對他進行排序,首先,我們如果A[0~n/2]和A[n/2+1,n-1]為
以下是對以下下代碼中的merge()(合並)函數正確性的證明:
1.當第一次循環叠代的時候,i = L, A[L, i-1]為空。是有序的序列(空也算有序序列),而且含有i-L=0個LA[n1],RA[n2]的最小的數,這時c1 = c2 = 0, LA[c1]和RA[c2]均為彼此數組中的最小的元素。
2.如果第i次叠代的時候LA[c1] <= RA[c2], 這時LA[c1]便是還沒有被拷貝到A中的最小的元素。此時A中含有i-L個最小的元素。當運行A[i] = LA[c1]時,A中便含有i-L+1個最小的元素,然後添加c1和i進行下一次叠代。如果第一次時LA[c1] > RA[c2]。運行相似的過程。
3.循環結束後,i = r+1, 此時A中含有i-L = r-L+1個最小的元素。恰好是l~r全部的元素,而且已排好序,證畢。
//insertion_sort #include <iostream> using namespace std; const int inf = (1<<28); void print(int* A, int n) { for (int i = 0; i < n; i++) { cout << A[i] << " "; } cout << endl; } void merge(int *A, int l, int m, int r) { int n1 = m-l+1, n2 = r-m; int lA[(const int)(n1+1)], rA[(const int)(n2+1)]; for (int i = 0; i < n1; i++) { lA[i] = A[i+l]; } for (int i = 0; i < n2; i++) { rA[i] = A[m+1+i]; } lA[n1] = rA[n2] = inf; int c1 = 0, c2 = 0; for (int i = l; i <= r; i++) { if (lA[c1] <= rA[c2]) { A[i] = lA[c1++]; } else { A[i] = rA[c2++]; } } } void merge_sort(int *A, int l, int r) { if (l < r) { int m = (l + r) / 2; merge_sort(A, l, m); merge_sort(A, m+1, r); merge(A, l, m, r); } } int main() { int A[10] = {43,2,53,1,8,29,52,4,8,10}; cout << "before sorted: "; print(A, 10); merge_sort(A, 0, 10); cout << "after sorted: "; print(A, 10); return 0; }
算法導論學習筆記(2)-歸並排序