归并排序 java_马士兵说之归并排序
大家對于排序應該是挺熟悉的吧,馬士兵老師特意為排序出了一波視頻,當然文章是轉自博客園的,馬士兵老師的視頻觀看請點擊下方的了解更多
概要
本章介紹排序算法中的歸并排序。內容包括:
1. 歸并排序介紹
2. 歸并排序圖文說明
3. 歸并排序的時間復雜度和穩定性
4. 歸并排序實現
4.1 歸并排序C實現
4.2 歸并排序C++實現
4.3 歸并排序Java實現
更多排序和算法請參考:數據結構與算法系列 目錄
歸并排序介紹
將兩個的有序數列合并成一個有序數列,我們稱之為"歸并"。
歸并排序(Merge Sort)就是利用歸并思想對數列進行排序。根據具體的實現,歸并排序包括"從上往下"和"從下往上"2種方式。
1. 從下往上的歸并排序:將待排序的數列分成若干個長度為1的子數列,然后將這些數列兩兩合并;得到若干個長度為2的有序數列,再將這些數列兩兩合并;得到若干個長度為4的有序數列,再將它們兩兩合并;直接合并成一個數列為止。這樣就得到了我們想要的排序結果。(參考下面的圖片)
2. 從上往下的歸并排序:它與"從下往上"在排序上是反方向的。它基本包括3步:
① 分解 -- 將當前區間一分為二,即求分裂點 mid = (low + high)/2;
② 求解 -- 遞歸地對兩個子區間a[low...mid] 和 a[mid+1...high]進行歸并排序。遞歸的終結條件是子區間長度為1。
③ 合并 -- 將已排序的兩個子區間a[low...mid]和 a[mid+1...high]歸并為一個有序的區間a[low...high]。
下面的圖片很清晰的反映了"從下往上"和"從上往下"的歸并排序的區別。
歸并排序圖文說明
歸并排序(從上往下)代碼
/* * 將一個數組中的兩個相鄰有序區間合并成一個 * * 參數說明: * a -- 包含兩個有序區間的數組 * start -- 第1個有序區間的起始地址。 * mid -- 第1個有序區間的結束地址。也是第2個有序區間的起始地址。 * end -- 第2個有序區間的結束地址。 */void merge(int a[], int start, int mid, int end){ int *tmp = (int *)malloc((end-start+1)*sizeof(int)); // tmp是匯總2個有序區的臨時區域 int i = start; // 第1個有序區的索引 int j = mid + 1; // 第2個有序區的索引 int k = 0; // 臨時區域的索引 while(i <= mid && j <= end) { if (a[i] <= a[j]) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } while(i <= mid) tmp[k++] = a[i++]; while(j <= end) tmp[k++] = a[j++]; // 將排序后的元素,全部都整合到數組a中。 for (i = 0; i < k; i++) a[start + i] = tmp[i]; free(tmp);}/* * 歸并排序(從上往下) * * 參數說明: * a -- 待排序的數組 * start -- 數組的起始地址 * endi -- 數組的結束地址 */void merge_sort_up2down(int a[], int start, int end){ if(a==NULL || start >= end) return ; int mid = (end + start)/2; merge_sort_up2down(a, start, mid); // 遞歸排序a[start...mid] merge_sort_up2down(a, mid+1, end); // 遞歸排序a[mid+1...end] // a[start...mid] 和 a[mid...end]是兩個有序空間, // 將它們排序成一個有序空間a[start...end] merge(a, start, mid, end);}從上往下的歸并排序采用了遞歸的方式實現。它的原理非常簡單,如下圖:
通過"從上往下的歸并排序"來對數組{80,30,60,40,20,10,50,70}進行排序時:
1. 將數組{80,30,60,40,20,10,50,70}看作由兩個有序的子數組{80,30,60,40}和{20,10,50,70}組成。對兩個有序子樹組進行排序即可。
2. 將子數組{80,30,60,40}看作由兩個有序的子數組{80,30}和{60,40}組成。
將子數組{20,10,50,70}看作由兩個有序的子數組{20,10}和{50,70}組成。
3. 將子數組{80,30}看作由兩個有序的子數組{80}和{30}組成。
將子數組{60,40}看作由兩個有序的子數組{60}和{40}組成。
將子數組{20,10}看作由兩個有序的子數組{20}和{10}組成。
將子數組{50,70}看作由兩個有序的子數組{50}和{70}組成。
歸并排序(從下往上)代碼
/* * 對數組a做若干次合并:數組a的總長度為len,將它分為若干個長度為gap的子數組; * 將"每2個相鄰的子數組" 進行合并排序。 * * 參數說明: * a -- 待排序的數組 * len -- 數組的長度 * gap -- 子數組的長度 */void merge_groups(int a[], int len, int gap){ int i; int twolen = 2 * gap; // 兩個相鄰的子數組的長度 // 將"每2個相鄰的子數組" 進行合并排序。 for(i = 0; i+2*gap-1 < len; i+=(2*gap)) { merge(a, i, i+gap-1, i+2*gap-1); } // 若 i+gap-1 < len-1,則剩余一個子數組沒有配對。 // 將該子數組合并到已排序的數組中。 if ( i+gap-1 < len-1) { merge(a, i, i + gap - 1, len - 1); }}/* * 歸并排序(從下往上) * * 參數說明: * a -- 待排序的數組 * len -- 數組的長度 */void merge_sort_down2up(int a[], int len){ int n; if (a==NULL || len<=0) return ; for(n = 1; n < len; n*=2) merge_groups(a, len, n);}從下往上的歸并排序的思想正好與"從下往上的歸并排序"相反。如下圖:
通過"從下往上的歸并排序"來對數組{80,30,60,40,20,10,50,70}進行排序時:
1. 將數組{80,30,60,40,20,10,50,70}看作由8個有序的子數組{80},{30},{60},{40},{20},{10},{50}和{70}組成。
2. 將這8個有序的子數列兩兩合并。得到4個有序的子樹列{30,80},{40,60},{10,20}和{50,70}。
3. 將這4個有序的子數列兩兩合并。得到2個有序的子樹列{30,40,60,80}和{10,20,50,70}。
4. 將這2個有序的子數列兩兩合并。得到1個有序的子樹列{10,20,30,40,50,60,70,80}。
歸并排序的時間復雜度和穩定性
歸并排序時間復雜度
歸并排序的時間復雜度是O(N*lgN)。
假設被排序的數列中有N個數。遍歷一趟的時間復雜度是O(N),需要遍歷多少次呢?
歸并排序的形式就是一棵二叉樹,它需要遍歷的次數就是二叉樹的深度,而根據完全二叉樹的可以得出它的時間復雜度是O(N*lgN)。
歸并排序穩定性
歸并排序是穩定的算法,它滿足穩定算法的定義。
算法穩定性 -- 假設在數列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。則這個排序算法是穩定的!
歸并排序實現
下面給出歸并排序的三種實現:C、C++和Java。這三種實現的原理和輸出結果都是一樣的,每一種實現中都包括了"從上往下的歸并排序"和"從下往上的歸并排序"這2種形式。
歸并排序C實現
實現代碼(merge_sort.c)
View Code
歸并排序C++實現
實現代碼(MergeSort.cpp)
View Code
歸并排序Java實現
實現代碼(MergeSort.java)
View Code
上面3種實現的原理和輸出結果都是一樣的。下面是它們的輸出結果:
before sort:80 30 60 40 20 10 50 70 after sort:10 20 30 40 50 60 70 80文章原作者:http://www.cnblogs.com/skywang12345/p/3602369.html
總結
以上是生活随笔為你收集整理的归并排序 java_马士兵说之归并排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: qsplitter 折叠_河南断桥折叠门
- 下一篇: tlab java_浅析java中的TL