合併排序詳解

2021-07-11 11:16:35 字數 3469 閱讀 7556

—————————————————————————————————————–

—————————————————————————————————————–

插入歸併

歸併排序的時間複雜度為o(nlgn),空間複雜度為o(n);

但是一般來講,基於從單個記錄開始兩兩歸併的排序並不是特別提倡,一種比較常用的改進就是結合插入排序,即先利用插入排序獲得較長的有序子串行,然後再兩兩歸併(改進後的歸併亦是穩定的,因為插入排序是穩定的)。之所以這樣改進是有原因的:儘管插入排序的最壞情況是o(n^2),看起來大於歸併的最壞情況o(nlgn),但通常情況下,由於插入排序的常數因子使得它在n比較小的情況下能執行的更快一些,因此,歸併時,當子問題足夠小時,採用插入排序是比較合適的。

複雜度分析

下面分析下插入歸併排序最壞情況下的複雜度:假設整個序列長度為n,當子串行長度為k時,採取插入排序策略,這樣一共有n/k個子序列。

子串行完成排序複雜度:最壞情況下,n/k個子序列完成排序的時間複雜度為o(nk)。證明:每個子串行完成插入排序複雜度為o(k^2),一共n/k個子序列,故為o(nk)。

子串行完成合併複雜度:最壞情況下,合併兩個子串行的時間複雜度為o(n),一共有n/k個子序列,兩兩合併,共需要lg(n/k)步的合併,所以這些子串行完成合併的複雜度為o(nlg(n/k))。

所以改進後的插入歸併排序的最壞情況的複雜度為o(nk+nlg(n/k)),這裡k的最大取值不能超過lgn,顯然如果k大於lgn,複雜度就不是與歸併乙個級別了,也就是說假設乙個1000長度的陣列,採用插入策略排序子串行時,子串行的最大長度不能超過10。

/* 二路歸併

*在函式外申請乙個臨時陣列作為引數傳入

*/void merge(int arr, int beg, int mid, int end, int temp_arr)

else

}

while(i <= mid)

while(j <= end)

}

void mergesort(int arr, int beg, int end,int temp_arr)

}

/* 改進的歸併演算法:插入歸併

* 先通過插入排序得到較長的有序串,然後歸併

* 即,當分解的陣列長度小於一定值時,不再分解,改用插入排序

*/#define insert_bound 5

void insertsort(int arr, int beg, int end)

arr[j+1] = temp;

} }void insert_mergesort(int arr, int beg,int,end, int temp_arr)

else

}

原地歸併下面舉例說明一種原地歸併排序的思想。

我們知道,無論是基於單個記錄的兩兩歸併,還是利用插入排序先得到較長的子串行然後歸併,在演算法合併的過程中,我們都是在合併「兩個相鄰的有序子串行」。

在了解原地歸併的思想之前,先回憶一下一般的歸併演算法,先是將有序子串行分別放入臨時陣列,然後設定兩個指標依次從兩個子串行的開始尋找最小元素放入歸併陣列中;那麼原地歸併的思想亦是如此,就是歸併時要保證指標之前的數字始終是兩個子串行中最小的那些元素。文字敘述多了無用,見示例**,一看就明白。

假設我們現在有兩個有序子串行如圖a,進行原地合併的**示例如圖b開始

如圖b,首先第乙個子串行的值與第二個子串行的第乙個值20比較,如果序列一的值小於20,則指標i向後移,直到找到比20大的值,即指標i移動到30;經過b,我們知道指標i之前的值一定是兩個子串行中最小的塊。

如圖c,先用乙個臨時指標記錄j的位置,然後用第二個子串行的值與序列一i所指的值30比較,如果序列二的值小於30,則j後移,直到找到比30大的值,即j移動到55的下標;

如圖d,經過圖c的過程,我們知道陣列塊[index, j)中的值一定是全部都小於指標i所指的值30,即陣列塊[index, j)中的值全部小於陣列塊[i, index)中的值,為了滿足原地歸併的原則:始終保證指標i之前的元素為兩個序列中最小的那些元素,即i之前為已經歸併好的元素。我們交換這兩塊陣列的記憶體塊,交換後i移動相應的步數,這個「步數」實際就是該步歸併好的數值個數,即陣列塊[index, j)的個數。從而得到圖e如下:

重複上述的過程,如圖f,相當於圖b的過程,直到最後,這就是原地歸併的一種實現思想,具體**如下。

/* 原地歸併

* 可二路歸併亦可與插入排序相結合,如**

*///reverse array

void

reverse(int arr, int size)

}// swap [arr,arr+headsize) and [arr+headsize,arr+headsize+endsize)

void swapmemory(int arr, int headsize, int endsize)

void inplace_merge(int arr, int beg, int mid, int end)

intindex = j;

while(j <= end && arr[j] <= arr[i])

swapmemory(&arr[i], index-i, j-index);//swap [i,index) and [index,j)

i += (j-index);

}}void inplace_mergesort(int arr, int beg, int end)

/* // 結合插入排序

if(end - beg + 1 <= insert_bound)

else

*/}/* 簡單測試用例 */

void main()

; int temp_arr = ;

/* 測試不同的歸併演算法 */

//mergesort(arr,0,8,temp_arr);

//insert_mergesort(arr,0,8,temp_arr);

//inplace_mergesort(arr,0,8);

for(int i = 0; i < 9; ++i)

cout對於原地歸併,它只是一種歸併的手段,具體實現歸併排序時,可以在二路歸併中使用原地歸併,也可以在基於插入排序改進的歸併演算法中採用,如**所示。

(全文完)

歸併排序 合併排序(merge sort)詳解

前提 1.待排序的子串行相對有序。2.不考慮大資料等特殊情況。includeusing namespace std void merge sort int a,int p,int r void merge int a,int p,int q,int r int b 20 int main int l...

歸併排序(合併排序)

題目 要求氣泡排序的交換次數,也就是求逆序數的個數。在乙個排列中如果有兩個數的排序和所規定的排序規則相反,則這兩個數是乙個逆序。乙個排列中的逆序的總數就是這個排列的逆序數。用歸併排序,求逆序數的個數。poj 2299 這道題充分印證了,即使merge本身可能用的不多,但分冶的思想卻是無所不在 inc...

歸併排序(合併排序)

合併排序 merge sort 是又一類不同的排序方法,合併的含義就是將兩個或兩個以上的有序資料序列合併成乙個新的有序資料序列,因此它又叫歸併演算法。它的基本思想就是假設陣列a有n個元素,那麼可以看成陣列a是又n個有序的子串行組成,每個子串行的長度為1,然後再兩兩合併,得到了乙個 n 2 個長度為2...