算法-Java實現歸併排序

歸併排序 (merge sort) 是一類與插入排序、交換排序、選擇排序不同的另一種排序方法。歸併的含義是將兩個或兩個以上的有序表合併成一個新的有序表。歸併排序有多路歸併排序、兩路歸併排序 , 可用於內排序,也可以用於外排序。這裏僅對內排序的兩路歸併方法進行討論。 
1.兩路歸併排序算法思路
①把 n 個記錄看成 n 個長度爲1的有序子表;
②進行兩兩歸併使記錄關鍵字有序,得到 n/2 個長度爲 2 的有序子表; 
③重複第②步直到所有記錄歸併成一個長度爲 n 的有序表爲止。

【例】 有一組關鍵字 {4,7,5,3,2,8,6,1},n=8, 將其按由小到大的順序排序。 兩路歸併排序操作過程如圖 9.12 所示,其中i 爲子表長度。

2.算法實現
  此算法的實現不像圖示那樣簡單,現分三步來討論。首先從宏觀上分析,首先讓子表表長 L=1 進行處理;不斷地使 L=2*L ,進行子表處理,直到 L>=n 爲止,把這一過程寫成一個主體框架函數 mergesort 。然後對於某確定的子表表長 L ,將 n 個記錄分成若干組子表,兩兩歸併,這裏顯然要循環若干次,把這一步寫成一個函數 mergepass ,可由 mergesort 調用。最後再看每一組(一對)子表的歸併,其原理是相同的,只是子表表長不同,換句話說,是子表的首記錄號與尾記錄號不同,把這個歸併操作作爲核心算法寫成函數 merge ,由 mergepass 來調用。假設我們有一個沒有排好序的序列,那麼首先我們使用分割的辦法將這個序列分割成一個一個已經排好序的子序列,然後再利用歸併的方法將一個個的子序列合併成排序好的序列。分割和歸併的過程可以看下面的圖例。



3.算法主要思想
template<class T>
  void merge( T r[],T r2[],int s,int mid,int t)
  //s爲第一個子表首元素的下標,mid爲第一個子表末元素的下標
  //t爲第二個子表末元素的下標
   { int i,j,k;
     i=s;j=mid+1;k=s;   //k是r2的初始指針
     while((i<=mid)&&(j<=t))
       { k=k+1;
         if(r[i].key<=r[j].key){r2[k]=r[i];i++;}
         else{r2[k]=r[j];j++;}
       }
     while(i<=mid){k++;r2[k]=r[i];i++;}
     while(j<=t){k++;r2[k]=r[j];j++;}
  }   //merge

4. 算法的Java實現

Java實現的二路歸併排序的算法如下:


public class myMergeSort {
    static int number=0;
    public static void main(String[] args) {
        int[] a = {26, 5, 98, 108, 28, 99, 100, 56, 34, 1 };
        printArray("排序前:",a);
        MergeSort(a);
        printArray("排序後:",a);
    }

    private static void printArray(String pre,int[] a) {
        System.out.print(pre+"\n");
        for(int i=0;i<a.length;i++)
            System.out.print(a[i]+"\t");    
        System.out.println();
    }

    private static void MergeSort(int[] a) {
        // TODO Auto-generated method stub
        System.out.println("開始排序");
        Sort(a, 0, a.length - 1);
    }

    private static void Sort(int[] a, int left, int right) {
        if(left>=right)
            return;
    
        int mid = (left + right) / 2;
        //二路歸併排序裏面有兩個Sort,多路歸併排序裏面寫多個Sort就可以了
        Sort(a, left, mid);
        Sort(a, mid + 1, right);
        merge(a, left, mid, right);

    }


    private static void merge(int[] a, int left, int mid, int right) {
    
        int[] tmp = new int[a.length];
        int r1 = mid + 1;
        int tIndex = left;
        int cIndex=left;
        // 逐個歸併
        while(left <=mid && r1 <= right) {
            if (a[left] <= a[r1]) 
                tmp[tIndex++] = a[left++];
            else
                tmp[tIndex++] = a[r1++];
        }
            // 將左邊剩餘的歸併
            while (left <=mid) {
                tmp[tIndex++] = a[left++];
            }
            // 將右邊剩餘的歸併
            while ( r1 <= right ) {
                tmp[tIndex++] = a[r1++];
            }
            
            
           
            
            System.out.println("第"+(++number)+"趟排序:\t");
            // TODO Auto-generated method stub
            //從臨時數組拷貝到原數組
             while(cIndex<=right){
                    a[cIndex]=tmp[cIndex];
                    //輸出中間歸併排序結果
                    System.out.print(a[cIndex]+"\t");
                    cIndex++;
                }
             System.out.println();
        }
    

    }

排序前:
26    5    98    108    28    99    100    56    34    1    
開始排序
第1趟排序:    
5    26    
第2趟排序:    
5    26    98    
第3趟排序:    
28    108    
第4趟排序:    
5    26    28    98    108    
第5趟排序:    
99    100    
第6趟排序:    
56    99    100    
第7趟排序:    
1    34    
第8趟排序:    
1    34    56    99    100    
第9趟排序:    
1    5    26    28    34    56    98    99    100    108    
排序後:
1    5    26    28    34    56    98    99    100    108     
5.算法分析
(1)穩定性
      歸併排序是一種穩定的排序。
(2)存儲結構要求
     可用順序存儲結構。也易於在鏈表上實現。
(3)時間複雜度
     對長度爲n的文件,需進行趟二路歸併,每趟歸併的時間爲O(n),故其時間複雜度無論是在最好情況下還是在最壞情況下均是O(nlgn)。
(4)空間複雜度
     需要一個輔助向量來暫存兩有序子文件歸併的結果,故其輔助空間複雜度爲O(n),顯然它不是就地排序。
  注意:
     若用單鏈表做存儲結構,很容易給出就地的歸併排序。
此程序需要注意地方有幾點:
1.理清算法思路,歸併和遞歸思想,遞歸一層一層由上到下,運行完再由下往上返回值。
2.return作用,如果return;返回就代表直接返回此函數;
3.實參形參變量命名,實參名字與形參名字對應,尤其是左數組和右數組的邊界值在中間值和中間值加一,
i=0
j=3
mind=i+j/2
mind=1
左數組範圍就在0,1
右數組在2,3
假設數組長度是4
因此注意範圍
4.檢驗變量變化,考察每次畢竟後i++的變化
此處直接在【】內i++顯得更節省內存和空間;
copy的數組隨着元素被放入也要i++變量的變化。
如果想弄清楚每一步執行情況debug也可以更方便。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章