1. 程式人生 > >歸併排序 筆試面試手寫程式碼常考

歸併排序 筆試面試手寫程式碼常考

歸併排序是將兩個或者兩個以上的有序序列進行合併的一種排序演算法。採用了分治的思想。

它的主要思路是將序列分為兩個子序列,對於兩個最終有序的子序列進行合併,得到有序的整體序列。

如何保證子序列有序呢?對子序列採用同樣的方式進行劃分,當子序列長度為1時,子序列有序,此時合併相鄰的子序列。

層層返回,不斷地進行合併,最終完整的序列就是一個有序的序列。

歸併排序可以很清晰地以遞迴的方式實現。


先進行劃分,分為兩個長度差不多的子序列,對每個子序列採用同樣的方式進行劃分,當劃分到序列長度為1時,此時序列便是有序的,我們便對相鄰的兩個有序序列進行合併,合併的方式很簡單,另外分配合適大小的輔助空間,將兩個有序序列的資料按照大小填充到輔助空間中,然後再從輔助空間將資料拷回原來的位置,這樣就完成了一次歸併。從最小的子序列開始,不同地進行歸併,最終便可以得到完整的有序序列。

程式碼的簡單實現如下:

/*歸併排序左邊小左邊,左邊 ++;右邊小取右邊,右邊 ++*/

template<typename T>

void merge(T array[],int low, int mid, inthigh)

{

  int k;

// 申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合併後的序列

  T*temp= new T[high-low+1];  

  intlbegin= low;

  int lend=mid;

  int rbegin=mid + 1;

  int rend=high;

  //比較兩個指標所指向的元素,選擇相對小的元素放入到合併空間,並移動指標到下一位置

  for (k=0; lbegin <=lend && rbegin <= rend; ++k)   

  {

     if(array[lbegin]<=array[rbegin])

 {

 temp[k] =array[lbegin++];

 }

     else

 {

temp[k] = array[rbegin++];

 }

  }

if(lbegin<= lend) // 若第一個序列有剩餘,直接拷貝出來粘到合併序列尾

    { 

memcpy(temp+k, array+lbegin, (lend-lbegin+1)*

sizeof(T));

}

if(rbegin<= rend) // 若第二個序列有剩餘,直接拷貝出來粘到合併序列尾

    {  

memcpy(temp+k, array+rbegin, (rend-rbegin+1)*sizeof(T));

}

    memcpy(array+low, temp, (high-low+1)*sizeof(T));//將排序好的序列拷貝回陣列中

    delete []temp;

}

template<typename T>

void merge_sort(T array[],unsigned int first, unsignedint last)

{

  int mid=0;

  if(first<last)

  {

//mid = (first+last)/2; /*注意防止溢位 */

     mid= first+(last-first) >> 1;

         //mid = (first & last) + ((first ^ last) >> 1);

     merge_sort(array,first, mid);

     merge_sort(array,mid+1,last);

     merge(array,first,mid,last);

  }

}


給出了程式碼之後,我們可以分析一下上述merge_sort的呼叫過程如下:


最後分析一下歸併排序的時間複雜度:


求解上述式子有:



即可知歸併排序的時間複雜度為O(nlogn),其空間複雜度為O(n)。