歸並排序 詳解


之前看了選擇和插入排序,這兩個算法是的時間復雜度均為O(n^2),而隨着問題規模n的增大,插入和選擇排序都比較慢。

     歸並排序時的時間復雜度為O(nlgn) 其主要思想是分治法(divide and conquer),分就是要將n個元素的序列划分為兩個序列,再將兩個序列划分為4個序列,

直到每個序列只有一個元素,最后,再將兩個有序序列歸並成一個有序的序列。

例如兩個序列:

要歸並成一個有序的序列,按照我們常規的方法,我們每次從兩個列表開頭元素選取較小的一個,直到某一個列表到達底部,再將另一個剩下部分順序取出。其實如果將每個元素最后添加一個最大值,則無需判斷是否達到列表盡頭。

代碼如下:merge函數的功能為將A中[low,mid],[mid+1,high]歸並成一個有序的片段。

template<class T> void merge(T A[],int low,int mid,int high)
{
    //low to mid as the left array   mid+1 to high as the right array
    int llen = mid-low+2;
    int rlen = high-mid+1;
    T *left = (T*)new T[llen];
    T *right = (T*)new T[rlen];
    //copy the low to mid to the temp array left
    for (int i=0;i<llen-1;i++)
    {
        left[i] = A[low+i];
    }
    for (i=0;i<rlen-1;i++)
    {
        right[i] = A[mid+1+i];
    }
    //set the sentinel
    left[llen-1] = numeric_limits<T>::max();
    right[rlen-1]= numeric_limits<T>::max();
    //merge the two array and copy to A[low,high];
    int j = 0;
    int k = 0;
    for (i = low; i < high+1 ;i++)
    {
        if (left[j] < right[k])
        {
            A[i] = left[j];
            j++;
        }
        else
        {
            A[i] = right[k];
            k++;
        }
    }
delete [] left;
delete [] right; }

歸並排序就是多次調用merge

template<class T> merge_sort(T A[],int low,int high)
{
    int mid = (low+high)/2;
    if (low < high)
    {
        merge_sort(A,low,mid);
        merge_sort(A,mid+1,high);
        merge(A,low,mid,high);
    }
}

這是一個遞歸算法,這個算法的理解其實可以借助下面這個圖:

對於原始的數組2,1,3,8,5,7,6,4,10,在整個過程執行的是順序是途中紅色編號1-20。雖然我們描述中說的是程序先分解,再歸並,但實際過程是一邊分解一邊歸並,前半部分分先排好序,后半部分再拍好,最后整個歸並為一個完整的序列,途中的merge過程它所在層的兩個序列的merge過程:下圖展示了每個merge過程對作用於數組的哪部分(紅色)。

整個過程就像一個動態的樹,執行順序就是對樹的先序遍歷順序。

 

最后實驗驗證,對10000個倒序的數組進行排序,直接插入排序需1024ms,二歸並排序只需20ms。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM