歸併排序之java實現

畢業季在即,一大波面試接踵而來,爲了一份好offer,所以最近在重新刷數據結構與算法。正好在博客裏面記錄一下。今天是歸併排序的實現。
歸併排序是典型的分治模式的實現,對一個數組A,採取三步實現:分解,解決,合併
分解:分解待排序的n個元素的序列成各具n/2個元素的兩個子序列
解決:使用歸併排序遞歸地排序兩個子序列
合併:合併兩個已排序的子序列產生最後排好序的序列
那麼到底什麼是歸併排序呢?
歸併排序的就是將兩個已經排好序的序列進行合併,使得合併後的序列仍是有序的。因爲在迭代中,序列中最開始只有一個元素,那麼它顯然是有序的,進行歸併後,在有序的基礎上仍然是有序的。
舉個例子:假設桌面上有兩堆面朝上的撲克牌,都已排好序。最小的牌在頂部。我們希望把兩堆牌合併成一堆排序好的牌,牌面朝下的放在桌子上。那麼我們自然想到這麼做:取兩堆牌頂上的較小的那張牌,放到桌子上(這時候取完牌的那堆牌已經露出了一張新的牌)。按照這個步驟繼續取牌。直到有一堆牌爲空爲止。這時候我們只需要把剩下的牌一張張的放桌子上就好了。所以基本需要常量時間,最多n個步驟,所以合併牌需要O(n)的時間。
可能有人會想到,什麼時候知道有堆牌爲空呢?難道需要每次拿牌的時候都去判斷是否爲空嗎?並不需要這樣做,在下面的僞代碼中可以看到使用了哨兵的概念,哨兵取爲無窮大,結果每當顯露一張值爲無窮大的牌的時候,顯然進入比較的If語句裏面,它不可能爲較小的牌,除非兩堆最頂上的牌都是無窮大,但是這種情況時,所有的非哨兵牌都已經放到桌子上了。已經放了r-p+1張牌,這個時候循環已經結束了。
歸併部分的僞代碼如下:

MERGE(A,p,q,r)
  n1=q-p+1;
  n2=r-q;
  let L[1...n1+1] and R[1...n2+1]
  for i=1 to n1
     L[i]=A(p+i-1);
  for j=1 to n2
     R[j]=A(q+j)
  L[n1+1]=
  R[n2+1]=
  i=1
  j=1
  for k=p to r
    if L[i]<=R[j]
      A[k]=L[i]
      i=i+1
    else
      A[k]=R[j]
      j=j+1

可以看到有這麼一行L[n1+1]=∞,這裏L[n1+1]=∞ ,這裏使用了哨兵的概念。這樣是爲了避免在每個基本步驟都需要去檢車是否有堆爲空,節省了效率
整個歸併排序的複雜度爲:O(nlgn)
Java實現如下:

private static void mergeSentry(int[] source, int begin, int middle, int end) {
        // TODO Auto-generated method stub
        int len1=middle-begin+1;
        int len2=end-middle;
        int[] array1=Arrays.copyOfRange(source,begin,middle+1);
        int[] array2=Arrays.copyOfRange(source, middle+1, end+1);
        int i = 0,j=0;
        for(int k=begin;k<=end;k++){
            if(i<len1&&j<len2){
                if(array1[i]<array2[j]||array1[i]==array2[j]){
                    source[k]=array1[i];
                    i++;
                    continue;
                }
                if(array1[i]>array2[j]){
                    source[k]=array2[j];
                    j++;
                    continue;
                }
            }
            if(i>=len1&&j<len2){
                source[k]=array2[j];
                j++;
                continue;
            }
            if(j>=len2&&i<len1){
                source[k]=array1[i];
                i++;
                continue;
            }
        }
    }

完整的僞代碼:

MERGE-SORT(A,p,r)
 if p<r
   q=(p+r)/2
   MERGE-SORT(A,p,q)
   MERGE-SORT(A,p+1,r)
   MERGE(A,p,q,r)

MERGE(A,p,q,r)
  n1=q-p+1;
  n2=r-q;
  let L[1...n1+1] and R[1...n2+1]
  for i=1 to n1
     L[i]=A(p+i-1);
  for j=1 to n2
     R[j]=A(q+j)
  L[n1+1]=
  R[n2+1]=
  i=1
  j=1
  for k=p to r
    if L[i]<=R[j]
      A[k]=L[i]
      i=i+1
    else
      A[k]=R[j]
      j=j+1

完整的java代碼:

public class MergeSort {
    public static void main(String[] args) {
        long begin=System.currentTimeMillis();
        int[] a=new int[]{1,22,2,12,120,9,87};
        mergerSort(a, 0, a.length-1);
        System.out.println(Arrays.toString(a));
    }
    public static void mergerSort(int[] source,int begin,int end){
        int middle;
        if(begin<end){
            middle=(int) (Math.floor((begin+end))/2);
            mergerSort(source, begin, middle);
            mergerSort(source, middle+1, end);
            mergeSentry(source,begin,middle,end);
        }
    }

    private static void mergeSentry(int[] source, int begin, int middle, int end) {
        // TODO Auto-generated method stub
        int len1=middle-begin+1;
        int len2=end-middle;
        int[] array1=Arrays.copyOfRange(source,begin,middle+1);
        int[] array2=Arrays.copyOfRange(source, middle+1, end+1);
        int i = 0,j=0;
        for(int k=begin;k<=end;k++){
            if(i<len1&&j<len2){
                if(array1[i]<array2[j]||array1[i]==array2[j]){
                    source[k]=array1[i];
                    i++;
                    continue;
                }
                if(array1[i]>array2[j]){
                    source[k]=array2[j];
                    j++;
                    continue;
                }
            }
            if(i>=len1&&j<len2){
                source[k]=array2[j];
                j++;
                continue;
            }
            if(j>=len2&&i<len1){
                source[k]=array1[i];
                i++;
                continue;
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章