歸併排序--排序演算法

涼茶coltea發表於2023-11-03

歸併排序

介紹

歸併排序和快速排序一樣,都是基於分治思想的應用。
透過遞迴,不斷將原數列分為兩個數列,然後再分別使其有序,最後透過歸併將兩個有序子數列合併為新的有序數列。
值得注意的是,與快速排序不同,歸併排序是穩定的。


程式碼實現

void merge_sort(int a[], int l, int r)
{
        if (l >= r) return;//判斷區間資料個數,為1則返回
        int  tmp[100001];//建立臨時陣列
        int mid = l + r >> 1;
    	merge_sort(a, l, mid);
    	merge_sort(a, mid + 1, r);
    	int k = 0, i = l, j = mid + 1;//建立雙指標
    	while (i <= mid && j <= r)//其中一指標遍歷至結尾則終止
                if (a[i] <= a[j]) tmp[k++] = a[i++];//對比資料,誰小先加誰
                else tmp[k++] = a[j++];
    	while(i<=mid) tmp[k++] = a[i++];
    	while(j<=r) tmp[k++] = a[j++];//將剩餘資料依次放入
    	for (i = l, j = 0; i <= r; i++, j++) a[i] = tmp[j];//將排好的資料放回原陣列
}

思路分析

void merge_sort(int a[], int l, int r)
{
        if (l >= r) return;//判斷區間資料個數,為1則返回
        int  tmp[100001];//建立臨時陣列
        int mid = l + r >> 1;
    	merge_sort(a, l, mid);
    	merge_sort(a, mid + 1, r);

和快速排序先排序後遞迴不同,歸併排序是先遞迴,無限細分,重點在於回溯時的歸併,當遞迴到陣列區間內只有1個資料時,肯定是有序的,經過歸併後返回的陣列肯定也是有序的,所以我們這裡假設這個函式已經能夠實現目的,將一個陣列分為兩部分然後分別排序,只需要用標記區間的起始位置和結束位置,而兩個區間的分界就是mid,而歸併的時候我們需要一個臨時存放資料的臨時陣列tmp[]


int k = 0, i = l, j = mid + 1;//建立雙指標
while (i <= mid && j <= r)//其中一指標遍歷至結尾則終止
        if (a[i] <= a[j]) tmp[k++] = a[i++];//對比資料,誰小先加誰
        else tmp[k++] = a[j++];

這裡便是整個演演算法最關鍵的地方:陣列的歸併。

首先,我們知道,兩個區間已經分別有序,而第一個區間起點便是本次函式傳入的起點l,終點為中值mid,第二個區間起點為mid+1,終點為函式傳入的終點引數r

我們建立雙指標lij,初始位置分別指向兩個取件的起始位置,不斷互相比較,值更小的放入陣列然後往後遍歷,這樣就能實現整個陣列的歸併,值得注意的是,我們的判斷條件為if(a[i]<=a[j]),這樣,就能保證同樣大小的資料按照原先的順序依次進入臨時陣列,實現歸併排序的穩定性。

最終,當其中一個指標遍歷到區間結尾時,結束迴圈。


while(i<=mid) tmp[k++] = a[i++];
while(j<=r) tmp[k++] = a[j++];//將剩餘資料依次放入
for (i = l, j = 0; i <= r; i++, j++) a[i] = tmp[j];//將排好的資料放回原陣列

當然,我們還需要將另外一個區間剩下的資料依次擠入臨時陣列,實現最終的排序,那我們就透過比較指標和終點的值來實現。

最後將排好的資料從臨時陣列中依次賦給原陣列。

至此,我們完成了整個歸併排序演演算法的閉環,完成了功能的實現。


結尾

歸併排序和快速排序都是分治思想的體現,他們的思路不僅僅能拿來排序,也能解決一些具體問題,具體選擇哪一種演演算法,就要依靠你細緻入微的觀察,結合題目的特性。

最後,歸併排序和快速排序的時間複雜度一樣,都是O(nlogn)

以上便是對歸併排序的介紹,本文由涼茶coltea撰寫,思路來自AcWing,大佬yxc的課程。

相關文章