歸併排序(merge sort)
是將兩個(或兩個以上)有序表合併成乙個新的有序表,即把待排序序列分為若干個有序的子串行,再把有序的子串行合併為整體有序序列。
歸併排序的具體做法:
把原序列不斷地遞迴等分,直至每等份只有乙個元素,此時每等份都是有序的。
相鄰等份合併,不斷合併,直至合併完全。
二路歸併
歸併排序
是建立在歸併操作上的一種有效的排序
演算法。該演算法是採用分治法
(divide and conquer)的乙個非常典型的應用。歸併排序最常用的是二路歸併,即把兩個小的有序的序列和並成乙個大的有序序列:合二為一。
乙個二路歸併的流程圖是這樣的:
多路歸併無非是多個有序的小序列合併成乙個大的有序序列,道理和二路歸併一樣。
先來看下如何把兩個有序的序列合併成乙個大的有序序列,**如下:
[cpp]view plain
copy
/**把有序序列a和b,合併成c
*該演算法成立前提: a和b已經有序
*/void
merge(
inta,
intna,
intb,
intnb,
intc)
//把a或b中剩餘的元素直接存入c
/* 也可以這樣:
* memcpy(c+k, a+i, (na-i)sizeof(int));
* 下同
*/while
(i c[k++] = a[i++];
while
(j c[k++] = b[j++];
} }
可以看出,二路歸併的時間複雜度是o(n),n是原序列的資料規模。以上**是歸併排序的基礎,弄懂了它,就很好寫歸併排序了,看下歸併排序的流程圖:
可以看出,上半部分不斷地遞迴深入:不斷地均分原序列,直到每一部分只含有乙個元素。下半部分,開始遞迴返回,通過反覆呼叫二路歸併演算法,把相鄰的有序子串行合併成乙個規模更大的序列。
理解了這些,相信就很容易寫出歸併排序的**了:
[cpp]view plain
copy
//把[first, mid]和[mid+1, last]範圍內的資料合併
void
mergearray(
inta,
intb,
intfirst,
intmid,
intlast)
/* 也可以這樣:
* memcpy(b+k, a+i, (mid-i+1)sizeof(int));
* 下同
*/while
(i <= mid)
b[k++] = a[i++];
while
(j <= last)
b[k++] = a[j++];
//[first,last]範圍內的資料已有序,則寫回原陣列
for(i = 0; i
a[first + i] = b[i];
} void
mergesort(
inta,
intb,
intfirst,
intlast)
} void
mergesort(
inta,
intn)
} 在排序過程中,我們使用了乙個相同大小的臨時輔助陣列。
演算法分析:
1.演算法的複雜度
對陣列長度為n的序列進行歸併排序,則大約要進行logn次歸併,每一次合併都是線性時間o(n)。故粗略的計算出歸併排序的時間複雜度是o(nlogn)(最好、最差都是這樣)。空間複雜度是o(n)。
詳細的時間複雜度分析是這樣的:
對長度為n的序列歸併排序,需要遞迴的對長度為n/2的子串行進行歸併排序,最後把兩段子序列二路歸併。遞推關係是這樣的:t(n)=2t(n/2)+o(n),顯然t(1)=o(1),解得t(n)=o(nlogn)。
2.穩定性
歸併排序是穩定的,並且是時間複雜度為o(nlogn)的幾種排序(快速排序、堆排序)中唯一穩定的排序演算法。
3.儲存結構
順序儲存和鏈式儲存都行。
另外,歸併排序多用於外排序中。
歸併排序(二路歸併)
歸併排序是一種遞迴思想的體現,通過多次合併較小的幾個 二路歸併為兩個 有序陣列形成新的有序表。思路 將陣列分為n nn個一元組,兩兩合併得到二元組,以此類推共合併log 2n log 2n log2 n 次後,陣列變得有序。時間複雜度為o n logn o nlogn o nlog n 編譯環境de...
排序 歸併排序(二路歸併)
基本思想 將兩個有序表合併成乙個有序表。將下列兩個已排序的順序表合併成乙個已排序表。順序比較兩 者的相應元素,小者移入另一表中,反覆如此,直至其中任一表都移入另 一表為止。二路歸併排序的基本思想是將兩個有序表合併成乙個有序表。給定排序碼46,55,13,42,94,05,17,70,二路歸併排序過程...
二路歸併排序
不是困難的演算法,不過也是練習了下遞迴。include include include using namespace std const int maxn 100 5 int a maxn int b maxn void mergesort int a,int b,int begin,int en...