歸併排序 非遞迴實現

2021-08-08 05:58:39 字數 3160 閱讀 4884

我們知道,遞迴實現的缺點就是會一直呼叫棧,而棧記憶體往往是很小的。所以,我們嘗試著用迴圈的辦法去實現歸併排序。

之氣提到過,歸併排序的基本思路是將待排序序列r[0…n-1]看成是n個長度為1的有序序列,將相鄰的有序表成對歸併,得到n/2個長度為2的有序表;將這些有序序列再次歸併,得到n/4個長度為4的有序序列;如此反覆進行下去,最後得到乙個長度為n的有序序列。

綜上可知:

歸併排序其實要做兩件事:

(1)「分解」——將序列每次折半劃分。

(2)「合併」——將劃分後的序列段兩兩合併後排序。

具體過程如下圖所示:

1.待排序序列r[0…n-1] 一共有n個有序的子串行,每個子串行裡都只含有乙個元素。

2. 某趟歸併中,設各子表的長度為length,則歸併前r[0…n-1]中共有n/length個有序的子表:r[0…length-1], r[gap…2*length-1], … , r[(n/length)*length … n-1]。

3. 在上面歸併的時候,我們遇到的length(1,2,4,8…)很可能不會在最終結果等於n。比如:n=7。這時候,我們採取的一種辦法是只歸併前面最接近n的那個位置(i+2*length<=n,即i<=n-2*length,處理到倒數第二對)。剩下的就通過下面**來實現:

if ( i+length

< n ) /*說明i+乙個子串行的長度*/

merge( a, tmpa, i, i+length, n-1);/* 歸併最後2個子列*/

else

/* 最後只剩1個子列*/

for ( j = i; j < n; j++ ) tmpa[j] = a[j];

n=7的示意圖:

完整**:

#include "stdio.h"

#include "stdlib.h"

void merge( int a, int tmpa, int l, int r, int rightend )

while( l <= leftend )

tmpa[tmp++] = a[l++]; /* 直接複製左邊剩下的 */

while( r <= rightend )

tmpa[tmp++] = a[r++]; /* 直接複製右邊剩下的 */

/*for( i = 0; i < numelements; i++, rightend -- )

a[rightend] = tmpa[rightend]; 將有序的tmpa複製回a */

}/* length = 當前有序子列的長度*/

void merge_pass( int a, int tmpa, int n, int length )

void merge_sort( int a, int n )

free( tmpa );

}else

printf( "空間不足" );

}int main()

; //int a[10]= ;

merge_sort(a,10);

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

printf("%d ",a[i]);

return

0;}

當然,我們在上面**的第24行中,將原先遞迴實現時每次歸併後儲存在tempa陣列的值傳回給a的步驟注釋了,同時在merge_sort( int a, int n )函式中,我們每次迴圈都merge_pass兩次:

while( length

< n )

這樣可以確保我們將序列歸併排序後的資料一定是儲存在陣列a中。

如果每次都在每次歸併的時候,加上那句for語句,那麼也就是每次都將排序結果傳回給陣列a,也能確保排序後的值在陣列a中。但是這樣處理的過程和原先的過程是有卻別的,現在的過程是:

歸併呼叫merge將相鄰的子表歸併時,必須對錶的特殊情況進行特殊處理:

若子表個數為奇數,則最後乙個子表無須和其他子表歸併(即本趟處理輪空):若子表個數為偶數,則要注意到最後一對子表中後乙個子表區間的上限為n-1。

示意圖如下:

從n=7和n=9的兩幅圖就可以看出,乙個是每次所有元素都進行歸併了,乙個是將殘留項(最後一項的長度小於length)保留,直到最後再歸併。

所以也可以將**改寫如下:

#include "stdio.h"

#include "stdlib.h"

void merge( int a, int tmpa, int l, int r, int rightend )

while( l <= leftend )

tmpa[tmp++] = a[l++]; /* 直接複製左邊剩下的 */

while( r <= rightend )

tmpa[tmp++] = a[r++]; /* 直接複製右邊剩下的 */

for( i = 0; i < numelements; i++, rightend -- )

a[rightend] = tmpa[rightend]; /*將有序的tmpa複製回a */

}/* length = 當前有序子列的長度*/

void merge_pass( int a, int tmpa, int n, int length )

void merge_sort( int a, int n )

free( tmpa );

}else

printf( "空間不足" );

}int main()

; //int a[10]= ;

merge_sort(a,10);

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

printf("%d ",a[i]);

return

0;}

歸併排序 非遞迴實現

1 歸併,非遞迴實現 1 段長度倍增,1,2,3,4,8,n 當要合併的單段長 2 以兩段的長度作為每次偏移的長度 每次合併是兩個單段一組進行合併,因此要不斷地偏移兩個單段的長度。5 3 5 2 8 7 2 3 5 7 8 include include include using namespac...

歸併排序(遞迴 非遞迴 自然歸併排序)

演算法思想 歸併排序是分治法的典型應用,其思想是不斷地將兩個有序的陣列合併為乙個有序陣列。遞迴實現 include void merge int a,int left,int m,int right void mergesortaux int a,int left,int right void me...

遞迴和非遞迴實現歸併排序

1 遞迴實現歸併排序 遞迴排序其實也是利用了完全二叉樹的結構形式,所以時間複雜度和logn掛鉤的。具體遞迴版的歸併排序呢,看下面的 public class ms print s mergesort s system.out.println 排序後的陣列 print s public static ...