2 路歸併排序

2021-06-21 14:30:24 字數 4032 閱讀 6337

歸併排序的時間複雜度為o(nlog2n),利用二路歸併排序時,需要利用與待排序序列長度相同的陣列作為臨時儲存單元,故該排序方法的空間複雜度o(n)。

由於二路歸併排序中,每兩個有序子串行合併成乙個有序序列時,若分別在兩個有序子串行中出現有相同關鍵字的記錄,則會使前乙個有序子串行中相同關鍵字的記錄先複製,後乙個有序子串行中相同關鍵字的記錄後複製,從而保持了他們的相對位置不變,因此,二路歸併排序是一種穩定的排序方法。

將兩個按值有序序列合併成乙個按值有序序列,則稱之為二路歸併排序,下面有自底向上和自頂向下的兩種排序演算法,自頂向下的排序在本文末講述,使用遞迴實現,**較簡潔,經供參考。

1. 歸併子演算法:把位置相鄰的兩個按值有序序列合併成乙個按值有序序列。例如把序列 x[s..u] = 和 序列 x[u+1..v] = 合併成序列 

z[s..v] = , 注意合併前的元素都位於同乙個有序序列的相鄰位置,合併後的有序序列的下標與合併前的序列總的起始下標相同。

演算法過程中需要三個整型變數,i 用來遍歷歸併的前乙個有序序列,其初始位置是s;j 用來遍歷歸併的後乙個有序序列,其初始值是u+1;q 用來指出歸併後得到的有序序列的末尾元素的位置,其初始值是s。當遍歷完成其中的乙個有序序列之後,只需把另乙個未結束有序序列的剩餘元素複製到合併後的有序序列的末尾。

看**:

[cpp]view plain

copy

print?

//將有序的x[s..u]和x[u+1..v]歸併為有序的z[s..v]

void

merge(

intx, 

intz, 

ints, 

intu, 

intv)  

while

( i <= u )   

//將x中剩餘元素x[i..u]複製到z

z[q++] = x[i++];  

while

( j <= v )   

//將x中剩餘元素x[j..v]複製到z

z[q++] = x[j++];  

}  

2. 一趟歸併掃瞄子演算法:將參加排序的序列分成若干個長度為 t 的,且各自按值有序的子串行,然後多次呼叫歸併子演算法merge將所有兩兩相鄰成對的子串行合併成若干個長度為

2t 的,且各自按值有序的子串行。

若某一趟歸併掃瞄到最後,剩下的元素個數不足兩個子串行的長度時:

看**:

[cpp]view plain

copy

print?

/* x[0..n-1]表示參加排序的初始序列

* t為某一趟歸併時子串行的長度

* 整型變數i指出當前歸併的兩個子串行中第1個子序列的第1個元素的位置

* y[0..n-1]表示這一趟歸併後的結果

*/void

mergepass(

intx, 

inty, 

intn, 

intt)  

if( n - i > t )       

//若最後剩下的元素個數大於乙個子串行的長度t時

merge(x, y, i, i + t - 1, n - 1);  

else

//n-i <= t時,相當於只是把x[i..n-1]序列中的資料賦值給y[i..n-1]

for( j = i ; j < n ; ++j )  

y[j] = x[j];  

}  

3. 二路歸併排序演算法:將參加排序的初始序列分成長度為1的子串行使用mergepass函式進行第一趟排序,得到 n / 2 個長度為 2 的各自有序的子串行(若n為奇數,還會存在乙個最後元素的子串行),再一次呼叫mergepass函式進行第二趟排序,得到 n / 4 個長度為 4 的各自有序的子串行, 第 i 趟排序就是兩兩歸併長度為 2^(i-1) 的子串行得到 n / (2^i) 長度為 2^i 的子串行,直到最後只剩乙個長度為n的子串行。由此看出,一共需要 log2n 趟排序,每一趟排序的時間複雜度是 o(n), 由此可知

該演算法的總的時間複雜度是是 o(n log2n),但是該演算法需要 o(n) 的輔助空間,空間複雜度很大,是 o(n).

看**:

[cpp]view plain

copy

print?

void

mergesort(

intx, 

intn)  

free(y);  

}  

程式總的**彙總:

[cpp]view plain

copy

print?

#include 

#include 

//將有序的x[s..u]和x[u+1..v]歸併為有序的z[s..v]

void

merge(

intx, 

intz, 

ints, 

intu, 

intv)  

while

( i <= u )   

//將x中剩餘元素x[i..u]複製到z

z[q++] = x[i++];  

while

( j <= v )   

//將x中剩餘元素x[j..v]複製到z

z[q++] = x[j++];  

}  /* x[0..n-1]表示參加排序的初始序列

* t為某一趟歸併時子串行的長度

* 整型變數i指出當前歸併的兩個子串行中第1個子序列的第1個元素的位置

* y[0..n-1]表示這一趟歸併後的結果

*/void

mergepass(

intx, 

inty, 

intn, 

intt)  

if( n - i > t )       

//若最後剩下的元素個數大於乙個子串行的長度t時

merge(x, y, i, i + t - 1, n - 1);  

else

//n-i <= t時,相當於只是把x[i..n-1]序列中的資料賦值給y[i..n-1]

for( j = i ; j < n ; ++j )  

y[j] = x[j];  

}  void

mergesort(

intx, 

intn)  

free(y);  

}  void

print_array(

intarray, 

intn)  

intmain()  

;  int

size = 

sizeof

(array) / 

sizeof

(int

);  

mergesort(array, size);  

print_array(array, size);  

return

0;  

}  

歸併排序自頂向下排序,僅供參考:

[cpp]view plain

copy

print?

void

merge2(

int*data, 

intstart, 

intmid, 

intend)  

while

(i <= mid)  

temp[k++] = data[i++];  

while

(j <= end)  

temp[k++] = data[j++];  

k = 0;  

for(i = start; i <= end; ++i)  

data[i] = temp[k++];  

free(temp);  

}  //從頂往下

void

mergesort2(

int*data, 

intstart, 

intend, 

int*des)    

歸併排序(2 路歸併排序)

遞迴寫法 include define maxn 100 void merge int a,int l1,int r1,int l2,int r2 將陣列a的區間 l1,r1 和區間 l2,r2 合併為乙個有序區間 else while i r1 while j r2 for int i 0 i非遞...

2 路歸併排序

假設初始序列含有n個記錄,則可看成是n個有序子串行,每個子串行的長度為1,然後兩兩歸併,得到 n 2 個長度為2或1的有序子串行 再兩兩歸併,如此重複,直至得到乙個長度為n的有序序列為止,這種排序方法稱為2 路歸併排序。2 路歸併排序中的核心操作是將一維陣列中前後相鄰的兩個有序序列歸併為乙個有序序列...

(2 路)歸併排序

2 路歸併的原理 將序列兩兩分組,將序列歸併為 n 2 向上取整 個組,組內單獨排序 然後將這些陣列再兩兩歸併,生成 n 4 向上取整 個組,組內再單獨排序 以此類推,直到只剩下乙個組為止。歸併排序的事件複雜度為 o nlogn 2 路歸併的遞迴寫法 include const int maxn 1...