常用算法(4)-歸併算法

形象視頻:http://v.youku.com/v_show/id_XMzMyODk5Njg4.html

 

將兩個有序的子序列[low,m]和[m,high]歸併成一個有序的序列。合併過程中,設置i,j和p三個指針,其初值分別指向這三個記錄區的起始位置。合併時以此比較arr[i]和arr[i],取較小的記錄複製到arr1[p],然後將i或j,以及p均+1。

void MergeSort(int *arr,int low,int m,int high)
{
    int i,j,p;
    int *arr1;//緩存向量
    i=low;
    j=m+1;
    p=0;
    while(i<=m&&j<=high)//兩序列非空時,將較小的輸出到向量arr1中
    {
        if(arr[i]<arr[j])
        {
            arr1[p]=arr[i];
            p++;
            i++
        }
        else
        {
            arr1[p]=arr[j];
            p++;
            j++;
        }
    }
    while(i<=m)//若該子序列非空,則將剩餘的記錄記錄到arr1中
    {
        arr[p]=arr1[i];
        p++;
        i++
    }
    while(j<=high)//若該子序列非空,則將剩餘的記錄記錄到arr1中
    {
        arr[p]=arr1[j];
        p++;
        j++;
    }
    for(i=low,p=0;i<=high;i++,p++)
        arr[i]=arr1[p];
}


1 自底向上的方法

基本思路:第一趟歸併排序時,將待排序的arr[0...n]看作是 n個長度爲1的有序子文件,將這些子序列兩兩歸併,若n爲偶數則得到n/2個長度爲2的有序序列;若n爲奇數,則最後一個子序列輪空(不參與歸併),因此本次歸併後得到錢lgn個有序子序列長度爲2,但最後一個子序列長度仍爲1。第二趟歸併則將第一趟歸併所得到lgn個有序子序列兩兩歸併,如此反覆,直到得到一個長度爲n的有序序列爲止。上述每次歸併,均是將兩個有序的子序列合併成一個有序子序列,故稱其爲“二路歸併排序”

代碼分析:在某趟歸併中,設各子序列長度爲length,則歸併序列arr[0..n]中有序子序列:[1..length],[length+1....2length],.....,[n/length-1]*length+1.....n]。

在調用歸併操作將相鄰的一對子序列進行歸併時,必須考慮兩個問題:1子序列個數是奇數,2最後一各子序列長度小於length,對應的作如下處理,

1.若子序列個數爲奇數,則最後一個子序列無須和其他子序列歸併,即本趟輪空;

2.若子序列個數爲偶數,要注意最後一對子序列中後一個序列的區間上界應該是n;

void MergePass(int *arr,int length)
{
   int i;
   //歸併長度爲length的兩個相鄰子序列
   for(i=1;i+2*length-1<=n;i=i+2*length)
 MergeSort(arr,i,i+length-1,i+2*length-1);
   //上述循環執行完後,arr數組中保存的是n/length個長度爲length的有序子序列
  //如果還剩下兩個子序列,後者的長度小於length,則設置後者的區間上限爲n
   if(i+length-1<n)
        MergeSort(arr,i,i+length-1,n)
  //如果i<=n&&i+length-1>=n,也即n爲奇數時,默認剩餘的一個那個元素無須歸併
}

//採用自底向上的方法,對arr[1....n]進行2路歸併
void Merge(int *arr)
{
  int length;
  for(length=1;length<n;length*=2)//作lgn趟歸併
 MergePass(arr,lenth);    
}


2 自頂向下的方法【待研究】

 

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


      設歸併排序的當前區間是R[low..high],分治法的三個步驟是:
①分解:將當前區間一分爲二,即求分裂點
                 
②求解:遞歸地對兩個子區間R[low..mid]和R[mid+1..high]進行歸併排序;
③組合:將已排序的兩個子區間R[low..mid]和R[mid+1..high]歸併爲一個有序的區間R[low..high]。
   遞歸的終結條件:子區間長度爲1(一個記錄自然有序)。


     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


 時間複雜度:對長度爲n的序列,需要進行lgn趟二路歸併,每趟歸併時間複雜度爲O(n),所以好或壞的情況下時間複雜度均爲O(nlgn)

空間複雜度:需要額外的向量保存結果,空間複雜度爲O(n)

屬於不穩定排序

 

參考:http://hi.baidu.com/sleet1986/blog/item/82f0ae8b6b4e38609f2fb4cd.html

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