歸併排序(Merge Sort)

一、定義

    歸併排序(Merge Sort)是利用"歸併"技術來進行排序。歸併是指將若干個已排序的子文件合併成一個有序的文件。算法

二、兩路歸併算法

(1)、算法基本思路

    設兩個有序的子文件(至關於輸入堆)放在同一貫量中相鄰的位置上:R[low..m],R[m+1..high],先將它們合併到一個局部的暫存向量R1(至關於輸出堆)中,待合併完成後將R1複製回R[low..high]中。spa

(1)合併過程

    合併過程當中,設置i,j和p三個指針,其初值分別指向這三個記錄區的起始位置。合併時依次比較R[i]和R[j]的關鍵字,取關鍵字較小的記錄複製到R1[p]中,而後將被複制記錄的指針i或j加1,以及指向複製位置的指針p加1。設計

    重複這一過程直至兩個輸入的子文件有一個已所有複製完畢(不妨稱其爲空),此時將另外一非空的子文件中剩餘記錄依次複製到R1中便可。指針

(2)動態申請R1

    實現時,R1是動態申請的,由於申請的空間可能很大,故須加入申請空間是否成功的處理。code

(2)、歸併算法

  void Merge(SeqList R,int low,int m,int high)
    {//將兩個有序的子文件R[low..m)和R[m+1..high]歸併成一個有序的
     //子文件R[low..high]
     int i=low,j=m+1,p=0; //置初始值
     RecType *R1; //R1是局部向量,若p定義爲此類型指針速度更快
     R1=(ReeType *)malloc((high-low+1)*sizeof(RecType));
     if(! R1) //申請空間失敗
       Error("Insufficient memory available!");
     while(i<=m&&j<=high) //兩子文件非空時取其小者輸出到R1[p]上
       R1[p++]=(R[i].key<=R[j].key)?R[i++]:R[j++];
     while(i<=m) //若第1個子文件非空,則複製剩餘記錄到R1中
       R1[p++]=R[i++];
     while(j<=high) //若第2個子文件非空,則複製剩餘記錄到R1中
       R1[p++]=R[j++];
     for(p=0,i=low;i<=high;p++,i++)
       R[i]=R1[p];//歸併完成後將結果複製回R[low..high]
    } //Merge

 歸併排序有兩種實現方法:自底向上和自頂向下。排序

三、歸併排序

一、 自底向上的方法

(1) 自底向上的基本思想

    自底向上的基本思想是:第1趟歸併排序時,將待排序的文件R[1..n]看做是n個長度爲1的有序子文件,將這些子文件兩兩歸併,若n爲偶數,則獲得 個長度爲2的有序子文件;若n爲奇數,則最後一個子文件輪空(不參與歸併)。故本趟歸併完成後,前 個有序子文件長度爲2,但最後一個子文件長度仍爲1;第2趟歸併則是將第1趟歸併所獲得的 個有序的子文件兩兩歸併,如此反覆,直到最後獲得一個長度爲n的有序文件爲止。遞歸

    上述的每次歸併操做,均是將兩個有序的子文件合併成一個有序的子文件,故稱其爲"二路歸併排序"。相似地有k(k>2)路歸併排序。ci

(2) 一趟歸併算法

    分析:class

    在某趟歸併中,設各子文件長度爲length(最後一個子文件的長度可能小於length),則歸併前R[1..n]中共有 個有序的子文件:R[1..length],R[length+1..2length],…, 。效率

    注意:

    調用歸併操做將相鄰的一對子文件進行歸併時,必須對子文件的個數多是奇數、以及最後一個子文件的長度小於length這兩種特殊狀況進行特殊處理:

    ① 若子文件個數爲奇數,則最後一個子文件無須和其它子文件歸併(即本趟輪空);

    ② 若子文件個數爲偶數,則要注意最後一對子文件中後一子文件的區間上界是n。

    具體算法以下:

    void MergePass(SeqList R,int length)
     { //對R[1..n]作一趟歸併排序
      int i;
      for(i=1;i+2*length-1<=n;i=i+2*length)
      Merge(R,i,i+length-1,i+2*length-1);
           //歸併長度爲length的兩個相鄰子文件
      if(i+length-1<n) //尚有兩個子文件,其中後一個長度小於length
         Merge(R,i,i+length-1,n); //歸併最後兩個子文件
      //注意:若i≤n且i+length-1≥n時,則剩餘一個子文件輪空,無須歸併
     } //MergePass

(3)二路歸併排序算法

  void MergeSort(SeqList R)
   {//採用自底向上的方法,對R[1..n]進行二路歸併排序
     int length;
     for(1ength=1;length<n;length*=2) //作 趟歸併

        MergePass(R,length); //有序段長度≥n時終止
   }

    注意:

    自底向上的歸併排序算法雖然效率較高,但可讀性較差。

二、自頂向下的方法

     採用分治法進行自頂向下的算法設計,形式更爲簡潔。

(1)分治法的三個步驟

    設歸併排序的當前區間是R[low..high],分治法的三個步驟是:

    ①分解:將當前區間一分爲二,即求分裂點

    ②求解:遞歸地對兩個子區間R[low..mid]和R[mid+1..high]進行歸併排序;

    ③組合:將已排序的兩個子區間R[low..mid]和R[mid+1..high]歸併爲一個有序的區間R[low..high]。

    遞歸的終結條件:子區間長度爲1(一個記錄天然有序)。

(2)具體算法

    void MergeSortDC(SeqList R,int low,int high)
     {//用分治法對R[low..high]進行二路歸併排序
       int mid;
       if(low<high){//區間長度大於1
          mid=(low+high)/2; //分解
          MergeSortDC(R,low,mid); //遞歸地對R[low..mid]排序
          MergeSortDC(R,mid+1,high); //遞歸地對R[mid+1..high]排序
          Merge(R,low,mid,high); //組合,將兩個有序區歸併爲一個有序區
        }
     }//MergeSortDC

(3)算法MergeSortDC的執行過程

    算法MergeSortDC的執行過程以下圖所示的遞歸樹。

四、算法分析

(1)、穩定性

    歸併排序是一種穩定的排序。

(2)、存儲結構要求

    可用順序存儲結構。也易於在鏈表上實現。

(3)、時間複雜度

    對長度爲n的文件,需進行 趟二路歸併,每趟歸併的時間爲O(n),故其時間複雜度不管是在最好狀況下仍是在最壞狀況下均是O(nlgn)。

(4)、空間複雜度

    須要一個輔助向量來暫存兩有序子文件歸併的結果,故其輔助空間複雜度爲O(n),顯然它不是就地排序。

    注意:

    若用單鏈表作存儲結構,很容易給出就地的歸併排序。。

相關文章
相關標籤/搜索