歸併排序的幾種變形

歸併排序的基礎概念就不講了,我的博客只會寫有創造性的東西。

 

歸併排序的代碼如下:

void mergesort(int* arr,int n){

if(n>1){

mergesort(arr,n/2);

mergesort(arr+n/2,n-n/2);

merge(buff,arr,n/2,arr+n/2,n-n/2);

memcpy(arr,buff,n);

}

}

 

迭代版

標準的歸併排序是個遞歸過程,不過要用迭代過程表達也很簡單,就是先兩個兩個一排,再四個四個一排。。。。

如:

98 15 73 20 76 27 34 82

15 98 20 73 27 76 34 82 //第一趟

15 20 73 98 27 34 76 82 //第二趟

15  20 27 34 73 76 82 98 //第三趟

 

代碼不寫了,代碼比遞歸版的複雜一點,效率也提高了一點

 

原地歸併算法

標準歸併排序使用的歸併算法要額外申請一個大小爲n的數組暫時存放歸併數據,原地歸併算法就不用,不過要以時間爲代價,它的一次歸併過程的時間複雜度是O(n^2)

 

舉個例子:

15 20 76 98 27 34 73 82

這裏有兩段有序數組,檢測到應該把273473插入到2076之間,也就是把

76 98 27 34 73 這段數組循環左移兩位。

如何把數組循環左移k位呢?

用的是翻大餅算法,先把前k個元素翻轉過來,再把後n-k個元素翻轉過來,在翻轉這n個元素。

過程如下:

15 20 76 98 27 34 73 82 //檢測到應該把27-73插入到2076之間

15 20 98 76 27 34 73 82 //翻轉9876這段數組

15 20 98 76 73 34 27 82 //翻轉273473這段數組

15 20 27 34 73 76 98 82 //翻轉這5個元素

15 20 27 34 73 76 98 82 //前面的數組已經有序,繼續歸併後面的數組

 

假設待排序元素是均勻分佈的,那麼待歸併的前後兩段有序數組元素是交叉排列,一次翻大餅算法只能插入O(1)個元素,因此原地歸併算法的時間複雜度是T(n)=T(n-1)+n,

得到T(n)=O(n^2)

 

那麼使用了原地歸併算法的歸併排序的時間複雜度就是T(n)=2T(n/2)+n^2,得到T(n)=n^2

原地歸併排序在效率上肯定是不如插入排序的,具體有什麼優勢暫時沒想到。

 

交替歸併排序

從標準歸併排序的代碼中可以看到,每次利用緩衝區完成歸併算法後,又要把緩衝區的內容複製回原數組裏面去,這浪費了一些時間。

利用緩衝區完成歸併算法後,完全可以把緩衝區和原數組的身份對調,就省去了複製回去的時間。

98 15 73 20 76 27 34 82 //A數組

15 98 20 73 27 76 34 82 //B數組

15 20 73 98 27 34 76 82 //A數組

15  20 27 34 73 76 82 98 //B數組

 

這裏有個問題,最後的有序序列在A數組還是在B數組,寫代碼的時候注意一下就可以了。

 

鏈表歸併排序

鏈表歸併排序很容易理解了,就是把前後兩半先排完,再用鏈表歸併算法。

 

鏈表歸併排序不存在數組歸併排序需要緩衝區的問題,好處還是很大的。

 

表歸併排序

這個肯定又是我的重新發明了

不知道大家有沒有看過嚴蔚敏寫的《數據結構(C語言版)》裏面的表插入排序呢?

同樣的,歸併排序也可以完美地加上一個“表”字。

剛纔說過,數組歸併排序需要額外申請一個緩衝區,鏈表歸併排序卻不存在這個問題。

在表歸併排序裏,申請緩衝區是必須的,但存的不是元素的副本,而是元素的下標,兩個數組形成一個靜態鏈表。

直接舉例子:

一開始數組無序,初始化下標數組,全都初始爲-1

0

1

2

3

4

5

6

7

98

15

73

20

76

27

34

82

-1

-1

-1

-1

-1

-1

-1

-1

 

第一趟歸併,變成兩個一組,15->98,20->73,27->76,34->82

0

1

2

3

4

5

6

7

98

15

73

20

76

27

34

82

-1

0

-1

2

-1

4

7

-1

 

 

 

 

第二趟歸併,變成四個一組,15->20->73->98,27->34->76->82

0

1

2

3

4

5

6

7

98

15

73

20

76

27

34

82

-1

3

0

2

7

6

4

-1

 

第三趟,成爲有序的靜態鏈表

0

1

2

3

4

5

6

7

98

15

73

20

76

27

34

82

-1

3

4

5

7

6

2

0

 

這裏又有個問題,需要記住每個鏈表的首元素的下標,這個很容易解決,這裏不討論了。

 

現在,鏈表已經有序了,還要把元素歸位,要用的是嚴蔚敏寫的《數據結構(C語言版)》重排記錄的算法。

描述有點複雜,這裏不寫了,想了解自己看書去吧。

 

這個表歸併排序算法,時間複雜度還是O(nlogn),空間複雜度還是O(n)

不過還是有一點優勢的,特別是對於元素塊頭比較大的數組。

時間上說,在排序時元素不需要移動,只有在歸位時,需要交換n-1次。

空間上說,申請的輔助空間只要存整型變量,不用存整個結構體。

也有點劣勢,它畢竟是鏈表,不符合空間局部性原理,cache對它就沒用了。

 

多路歸併

歸併多個有序數組,可以用堆,可以用敗者樹,也可以用哈夫曼樹,不寫了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章