排序七 归并排序
要點(diǎn)
歸并排序是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個(gè)非常典型的應(yīng)用。
將已有序的子序列合并,得到完全有序的序列;即先使每個(gè)子序列有序,再使子序列段間有序。若將兩個(gè)有序表合并成一個(gè)有序表,稱為二路歸并。
?
歸并排序的基本思想
將待排序序列R[0...n-1]看成是n個(gè)長度為1的有序序列,將相鄰的有序表成對歸并,得到n/2個(gè)長度為2的有序表;將這些有序序列再次歸并,得到n/4個(gè)長度為4的有序序列;如此反復(fù)進(jìn)行下去,最后得到一個(gè)長度為n的有序序列。
綜上可知:
歸并排序其實(shí)要做兩件事:
(1)“分解”——將序列每次折半劃分。
(2)“合并”——將劃分后的序列段兩兩合并后排序。
?
我們先來考慮第二步,如何合并?
在每次合并過程中,都是對兩個(gè)有序的序列段進(jìn)行合并,然后排序。
這兩個(gè)有序序列段分別為?R[low, mid]?和?R[mid+1, high]。
先將他們合并到一個(gè)局部的暫存數(shù)組R2中,帶合并完成后再將R2復(fù)制回R中。
為了方便描述,我們稱?R[low, mid]?第一段,R[mid+1, high]?為第二段。
每次從兩個(gè)段中取出一個(gè)記錄進(jìn)行關(guān)鍵字的比較,將較小者放入R2中。最后將各段中余下的部分直接復(fù)制到R2中。
經(jīng)過這樣的過程,R2已經(jīng)是一個(gè)有序的序列,再將其復(fù)制回R中,一次合并排序就完成了。
核心代碼
public?void?Merge(int[]?array,?int?low,?int?mid,?int?high)?{????int?i?=?low;?//?i是第一段序列的下標(biāo)
????int?j?=?mid?+?1;?//?j是第二段序列的下標(biāo)
????int?k?=?0;?//?k是臨時(shí)存放合并序列的下標(biāo)
????int[]?array2?=?new?int[high?-?low?+?1];?//?array2是臨時(shí)合并序列
????//?掃描第一段和第二段序列,直到有一個(gè)掃描結(jié)束
????while?(i?<=?mid?&&?j?<=?high)?{
????????//?判斷第一段和第二段取出的數(shù)哪個(gè)更小,將其存入合并序列,并繼續(xù)向下掃描
????????if?(array[i]?<=?array[j])?{
????????????array2[k]?=?array[i];
????????????i++;
????????????k++;
????????}?else?{
????????????array2[k]?=?array[j];
????????????j++;
????????????k++;
????????}
????}
????//?若第一段序列還沒掃描完,將其全部復(fù)制到合并序列
????while?(i?<=?mid)?{
????????array2[k]?=?array[i];
????????i++;
????????k++;
????}
????//?若第二段序列還沒掃描完,將其全部復(fù)制到合并序列
????while?(j?<=?high)?{
????????array2[k]?=?array[j];
????????j++;
????????k++;
????}
????//?將合并序列復(fù)制到原始序列中
????for?(k?=?0,?i?=?low;?i?<=?high;?i++,?k++)?{
????????array[i]?=?array2[k];
????}
}
掌握了合并的方法,接下來,讓我們來了解??如何分解。
在某趟歸并中,設(shè)各子表的長度為gap,則歸并前R[0...n-1]中共有n/gap個(gè)有序的子表:R[0...gap-1], R[gap...2*gap-1], ... , R[(n/gap)*gap ... n-1]。
調(diào)用Merge將相鄰的子表歸并時(shí),必須對表的特殊情況進(jìn)行特殊處理。
若子表個(gè)數(shù)為奇數(shù),則最后一個(gè)子表無須和其他子表歸并(即本趟處理輪空):若子表個(gè)數(shù)為偶數(shù),則要注意到最后一對子表中后一個(gè)子表區(qū)間的上限為n-1。?
核心代碼
public?void?MergePass(int[]?array,?int?gap,?int?length)?{????int?i?=?0;
????//?歸并gap長度的兩個(gè)相鄰子表
????for?(i?=?0;?i?+?2?*?gap?-?1?<?length;?i?=?i?+?2?*?gap)?{
????????Merge(array,?i,?i?+?gap?-?1,?i?+?2?*?gap?-?1);
????}
????//?余下兩個(gè)子表,后者長度小于gap
????if?(i?+?gap?-?1?<?length)?{
????????Merge(array,?i,?i?+?gap?-?1,?length?-?1);
????}
}
public?int[]?sort(int[]?list)?{
????for?(int?gap?=?1;?gap?<?list.length;?gap?=?2?*?gap)?{
????????MergePass(list,?gap,?list.length);
????????System.out.print("gap?=?"?+?gap?+?":\t");
????????this.printAll(list);
????}
????return?list;
}
算法分析
歸并排序算法的性能
| 排序類別 | 排序方法 | 時(shí)間復(fù)雜度 | 空間復(fù)雜度 | 穩(wěn)定性 | 復(fù)雜性 | ||
| 平均情況 | 最壞情況 | 最好情況 | |||||
| 歸并排序 | 歸并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 穩(wěn)定 | 較復(fù)雜 |
?
時(shí)間復(fù)雜度
歸并排序的形式就是一棵二叉樹,它需要遍歷的次數(shù)就是二叉樹的深度,而根據(jù)完全二叉樹的可以得出它的時(shí)間復(fù)雜度是O(n*log2n)。
?
空間復(fù)雜度
由前面的算法說明可知,算法處理過程中,需要一個(gè)大小為n的臨時(shí)存儲(chǔ)空間用以保存合并序列。
?
算法穩(wěn)定性
在歸并排序中,相等的元素的順序不會(huì)改變,所以它是穩(wěn)定的算法。
?
歸并排序和堆排序、快速排序的比較
若從空間復(fù)雜度來考慮:首選堆排序,其次是快速排序,最后是歸并排序。
若從穩(wěn)定性來考慮,應(yīng)選取歸并排序,因?yàn)槎雅判蚝涂焖倥判蚨际遣环€(wěn)定的。
若從平均情況下的排序速度考慮,應(yīng)該選擇快速排序。?
完整參考代碼
Java版本
?View Code
運(yùn)行結(jié)果?
gap?=?1:???1???9???3???5???2???4???6???8???7??
gap?=?2:???1???3???5???9???2???4???6???8???7??
gap?=?4:???1???2???3???4???5???6???8???9???7??
gap?=?8:???1???2???3???4???5???6???7???8???9??
排序后: ? ? 1???2???3???4???5???6???7???8???9 ?
本文轉(zhuǎn)自靜默虛空博客園博客,原文鏈接:http://www.cnblogs.com/jingmoxukong/p/4308823.html,如需轉(zhuǎn)載請自行聯(lián)系原作者
總結(jié)
- 上一篇: 如何在SQL Server数据库中加密数
- 下一篇: SSH框架中怎么使用Hibernate查