归并排序的分析与Java实现
歸并操作(merge),也叫歸并算法,指的是將兩個已經排序的序列合并成一個序列的操作。
歸并排序算法依賴歸并操作。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。
歸并排序在眾多排序算法中既是穩定排序,效率也比較高,同時,歸并排序不僅可以用于內排序,還可以用于外排序。
1.兩個有序數列的合并
設兩個有序數列放在同一向量中相鄰的位置上:R[low..m],R[m+1..high],先將它們合并到一個局部的暫存向量 R1中,待合并完成后將 R1 復制回 R[low..high]中。
(1)合并過程
合并過程中,設置 i,j 和 p 三個指針,其初值分別指向這三個記錄區的起始位置。
合并時依次比較 R[i]和 R[j]的關鍵字,取關鍵字較小的記錄復制到 R1[p]中,然后將被復制記錄的指針 i 或 j 加 1,以及指向復制位置的指針 p 加 1。
重復這一過程直至兩個輸入的子文件有一個已全部復制完畢,此時將另一非空的子文件中剩余記錄依次復制到 R1 中即可。
(2)動態申請 R1
實現時,R1 是動態申請的,因為申請的空間可能很大,所以在工程上應用時,可能需要加入申請空間是否成功的處理。
2.歸并排序的實現
(1)二路歸并的思路
將數組劃均分為兩個子數組;
對兩個字數組進行排序;
將排序好的兩個字數組歸并。
所謂 N路歸并 是指將數組均分為N個子數組,將字數組排序后再歸并。二路歸并是歸并排序的最一般的情況。
(2)歸并排序實現的一般化過程
申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合并后的序列
設定兩個指針,最初位置分別為兩個已經排序序列的起始位置
比較兩個指針所指向的元素,選擇相對小的元素放入到合并空間,并移動指針到下一位置
重復步驟3直到某一指針到達序列尾
將另一序列剩下的所有元素直接復制到合并序列尾
(3)實現過程
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | public?class?MergeSort { ????? ????public?static?void?main(String[] args){ ????????int[] arr=new?int[]{8,7,6,5,4,3,2,1}; ????????for(int?i=0;i<arr.length;i++){ ????????????System.out.print(arr[i]+",");?????????? ????????} ????????//想起引用傳遞和值傳遞的問題,數組是引用傳遞,不需要什么返回值 ????????sort(arr,arr.length); ????????System.out.println("排序后");? ????????for(int?i=0;i<arr.length;i++){ ????????????System.out.print(arr[i]+",");?????????? ????????} ????} ????? ????public?static?void?sort(int[] arr,int?n){ ????????if(arr==null?|| n<2){ ????????????return;//邊界情況 ????????} ????????divide(arr,0,n-1); ????} ????/** ?????* ?????* @param arr 待排序數組 ?????* @param left 待排序區間左側下標 ?????* @param right 待排序區間右側下標 ?????*/ ????public?static?void?divide(int[] arr,int?left,int?right){ ????????? ????????if(left>=right){ ????????????return; ????????} ????????int?m=(left+right)/2;//從中間開始分成兩個區間進行歸并 ????????divide(arr,left,m);//一直遞歸劃分直到區間長度為1 ????????divide(arr,m+1,right); ????????//開始逐級往上進行歸并操作 ????????binaryMerge(arr,left,m,right);//第一次進行merge操作的遞歸棧最深層此時是merge(arr,0,0,1) ????} ????? ????/** ?????* 對數組arr[left...right]位置進行遞歸的歸并排序 ?????* @param arr ?????* @param left ?????* @param rigtht ?????* @param temp ?????*/ ????public?static?void?binaryMerge(int[] arr,int?left,int?m,int?right){ ????????/** ?????????* 歸并排序的時間復雜度為O(n),指的就是最大長度為n的臨時數組 ?????????*/ ????????int[] temp=new?int[right-left+1]; ????????int?l=left;// ????????int?r=m+1;// ????????int?index=0;// ????????? ????????while(l<=m && r<=right){ ????????????if(arr[l]<arr[r]){ ????????????????temp[index]=arr[l]; ????????????????index++; ????????????????l++; ????????????}else{ ????????????????temp[index]=arr[r]; ????????????????index++; ????????????????r++; ????????????} ????????} ????????? ????????/** ?????????* 交換完了如果哪一側數組還有剩余,則全部賦值 ?????????* 實際的一次歸并下面的兩個while循環只會執行一個 ?????????* warn!不要漏了等于的情況!否則會每兩個元素丟失一個元素 ?????????*/ ????????while(l<=m){ ????????????temp[index]=arr[l]; ????????????index++; ????????????l++; ????????} ????????? ????????while(r<=right){ ????????????temp[index]=arr[r]; ????????????index++; ????????????r++; ????????} ????????? ????????/** ?????????* 用臨時數組的部分排序的序列全部替換待排序數組中對應的部分 ?????????* 這里應該用temp.length,不能用arr.length ?????????*/ ????????for(int?i=0;i<temp.length;i++){ ????????????//注意是left,此時的l是已經變化了的 ????????????arr[left+i]=temp[i]; ????????} ????????? ????} ????? } |
(4)時間和空間復雜度
歸并排序的最好、最壞和平均時間復雜度都是O(nlogn),而空間復雜度是O(n),
比較次數介于(nlogn)/2和(nlogn)-n+1,賦值操作的次數是(2nlogn)。因此可以看出,歸并排序算法比較占用內存,但卻是效率高且穩定的排序算法。
3.空間復雜度為O(1)的歸并排序
歸并排序的空間復雜度為O(N),
通過優化可以把空間復雜度降為O(1),通過手搖算法,
但實際上通過手搖算法后,此時的時間復雜度會上升。
4.多路歸并排序
歸并排序的思想可以用于外排序。
外排序是相對內排序而言的,在常規的小規模排序過程中,都是直接在內存中對數據進行排序處理的,而對于數據量極大的排序問題,這種方式是不現實的。
這個時候就要通過外排序來進行,先將數據劃分成多個規模能在內存中處理的子集,對各個子集排序后存放在臨時的磁盤文件上,然后再將這些子集歸并到輸出文件中。
這個過程要使用到多路歸并。
?
本文轉自邴越博客園博客,原文鏈接:http://www.cnblogs.com/binyue/p/3421123.html,如需轉載請自行聯系原作者
總結
以上是生活随笔為你收集整理的归并排序的分析与Java实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TiKV 源码解析系列
- 下一篇: [00级花岗石平台]00级花岗石平台的形