标题: 归并排序 [打印本页] 作者: 韩冰 时间: 2004-10-4 05:16 标题: 归并排序 <>可以运用分而治之方法来解决排序问题,该问题是将n 个元素排成非递减顺序。分而治之方法通常用以下的步骤来进行排序算法:若n 为1,算法终止;否则,将这一元素集合分割成两个或更多个子集合,对每一个子集合分别排序,然后将排好序的子集合归并为一个集合。 ( k( J M5 o, _7 n6 c 6 e$ V- j }9 ]5 Z+ Z假设仅将n 个元素的集合分成两个子集合。现在需要确定如何进行子集合的划分。一种可能性就是把前面n- 1个元素放到第一个子集中(称为A),最后一个元素放到第二个子集里(称为B)。按照这种方式对A递归地进行排序。由于B仅含一个元素,所以它已经排序完毕,在A排完序后,只需要用程序2 - 1 0中的函数i n s e r t将A和B合并起来。把这种排序算法与I n s e r t i o n S o r t(见程序2 - 1 5)进行比较,可以发现这种排序算法实际上就是插入排序的递归算法。该算法的复杂性为O (n 2 )。把n 个元素划分成两个子集合的另一种方法是将含有最大值的元素放入B,剩下的放入A中。然后A被递归排序。为了合并排序后的A和B,只需要将B添加到A中即可。假如用函数M a x(见程序1 - 3 1)来找出最大元素,这种排序算法实际上就是S e l e c t i o n S o r t(见程序2 - 7)的递归算法。* O- F5 H* e; z( k0 u$ ~" C
' F Y( `8 }! N6 H假如用冒泡过程(见程序2 - 8)来寻找最大元素并把它移到最右边的位置,这种排序算法就是B u b b l e S o r t(见程序2 - 9)的递归算法。这两种递归排序算法的复杂性均为(n2 )。若一旦发现A已经被排好序就终止对A进行递归分割,则算法的复杂性为O(n2 )(见例2 - 1 6和2 - 1 7)。 1 u; D' Q! W4 o. v9 V# h- \- c ) V2 w H: D6 Y: d4 O; m0 t上述分割方案将n 个元素分成两个极不平衡的集合A和B。A有n- 1个元素,而B仅含一个元素。下面来看一看采用平衡分割法会发生什么情况: A集合中含有n/k 个元素,B中包含其余的元素。递归地使用分而治之方法对A和B进行排序。然后采用一个被称之为归并( m e rg e)的过程,将已排好序的A和B合并成一个集合。 ! a+ [3 K- i, [ U, u+ ~7 ^; g4 d' X# F* N
例2-5 考虑8个元素,值分别为[ 1 0,4,6,3,8,2,5,7 ]。如果选定k = 2,则[ 1 0 , 4 , 6 , 3 ]和[ 8 , 2 , 5 , 7 ]将被分别独立地排序。结果分别为[ 3 , 4 , 6 , 1 0 ]和[ 2 , 5 , 7 , 8 ]。从两个序列的头部开始归并这两个已排序的序列。元素2比3更小,被移到结果序列;3与5进行比较,3被移入结果序列;4与5比较,4被放入结果序列;5和6比较,.。如果选择k= 4,则序列[ 1 0 , 4 ]和[ 6 , 3 , 8 , 2 , 5 , 7 ]将被排序。排序结果分别为[ 4 , 1 0 ]和[ 2 , 3 , 5 , 6 , 7 , 8 ]。当这两个排好序的序列被归并后,即可得所需要的排序序列。 3 m) u% q7 _4 x7 U& U: l ' E; b1 r9 @' u( I! i6 T图2 - 6给出了分而治之排序算法的伪代码。算法中子集合的数目为2,A中含有n/k个元素。 . `# L. H5 H" Q( s/ G8 B3 K, P0 N4 E& n
template<CLASS T> - s G+ o+ ?) a" Q5 \8 j' F. t5 [" @# z- g$ U2 F
void sort( T E, int n) 7 l7 H' u2 K* _2 S2 I8 G& P8 W 8 \+ L" P @! R$ P{ / /对E中的n 个元素进行排序, k为全局变量0 {3 V8 c# B& x+ d7 b' F E& o0 m3 y
5 G" h: N! ] T, G, l' b2 B1 W$ ?
if (n >= k) { ( I8 j0 ]2 Z: U; [! i( a5 [4 M+ H9 o1 }" K
i = n/k;4 `! j) {# k3 Z9 ~2 ~9 `
; \9 h, J9 z. ~/ z* m# N! f" k5 Ys += s; ' ?2 [# ?! g: m ! |( o, D b1 w}1 C, U2 {2 N1 u
! o/ R+ s# f4 N1 r* H}+ b+ o: H# M% `: D- o
/ I G0 {; M; a0 l0 C {) x. y' W
为了完成排序代码,首先需要完成函数M e rg e P a s s。函数M e rg e P a s s(见程序1 4 - 4)仅用来确定欲归并子序列的左端和右端,实际的归并工作由函数M e rg e (见程序1 4 - 5 )来完成。函数M e rg e要求针对类型T定义一个操作符< =。如果需要排序的数据类型是用户自定义类型,则必须重载操作符< =。这种设计方法允许我们按元素的任一个域进行排序。重载操作符< =的目的是用来比较需要排序的域。 % f3 ]$ o; z6 o3 S; r3 \* n0 G . {1 k, V E% g5 F- [ o9 z程序14-4 MergePass函数) H/ T% J4 y% ~1 X1 h/ i
8 V: b! \, `, X程序14-5 Merge函数, t F/ V, W7 d8 }3 w5 v
+ W4 s& ~2 w, H! o; W$ ?( X6 w
template<CLASS T> _7 p7 Z o# I : `3 i& F. E. Qvoid Merge(T c[], T d[], int l, int m, int r) & z* t4 l2 C c/ \' j7 p7 m, ]% i; N; Z
{// 把c[l:m]] 和c[m:r] 归并到d [ l : r ] . - C# i( g# K7 M, J. t3 i 2 n8 S+ ~: s( I* E) H( t, Hint i = l, // 第一段的游标- E" t; R; }: j8 Y6 k
0 \& q! ~7 u$ U7 ~* u2 q8 [3 Fj = m+1, // 第二段的游标# M1 j3 @. E0 C
4 G% l/ w) \3 G1 Bk = l; // 结果的游标7 @; \; H T& c5 F1 H7 L3 o. ]- w
8 U( O7 W7 a) y) w
/ /只要在段中存在i和j,则不断进行归并& T4 ~) a: w5 E+ k/ v8 O7 y+ M
_7 ^4 \" i% M0 f$ u* Q. D) Q
while ((i <= m) && (j <= r)) 7 W* x7 u8 N! |/ i9 a 5 o1 _% n9 d6 k6 E2 ?) rif (c <= c[j]) d[k++] = c[i++]; " l I$ g+ D6 i- T; U+ ] 2 |2 t9 w: ^; e. L" Jelse d[k++] = c[j++]; ! s, C* R9 c C8 @# s8 S6 l3 n; |+ I3 @3 ~; [+ y
// 考虑余下的部分0 P, {! y6 P( t! f: Y& r, T- O7 n
/ Y- ~) h( G3 n( K- C- s
if (i > m) for (int q = j; q <= r; q++)1 Q4 v9 V8 u* [* x D' F$ J
; M/ h; b0 _8 @( c: {
d[k++] = c[q]; 4 k' t3 t. v: K1 q ! p1 _% J7 b" ~$ @* z+ Kelse for (int q = i; q <= m; q++)* `" ^. Q. L& L, w% {. h
" Y# E z' C* V2 |9 r; U
d[k++] = c[q]; i2 ?5 p: f: B2 o! ^6 o. @( j" I
7 B D7 I7 J" J5 w7 ]}9 y' F6 T/ F9 J' c) H6 ^ _
, D$ a D1 h6 D' I. d8 a
自然归并排序(natural merge sort)是基本归并排序(见程序1 4 - 3)的一种变化。它首先对输入序列中已经存在的有序子序列进行归并。例如,元素序列[ 4,8,3,7,1,5,6,2 ]中包含有序的子序列[ 4,8 ],[ 3,7 ],[ 1,5,6 ]和[ 2 ],这些子序列是按从左至右的顺序对元素表进行扫描而产生的,若位置i 的元素比位置i+ 1的元素大,则从位置i 进行分割。对于上面这个元素序列,可找到四个子序列,子序列1和子序列2归并可得[ 3 , 4 , 7 , 8 ],子序列3和子序列4归并可得[ 1 , 2 , 5 , 6 ],最后,归并这两个子序列得到[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]。因此,对于上述元素序列,仅仅使用了两趟归并,而程序1 4 - 3从大小为1的子序列开始,需使用三趟归并。作为一个极端的例子,假设输入的元素序列已经排好序并有n个元素,自然归并排序法将准确地识别该序列不必进行归并排序,但程序1 4 - 3仍需要进行[ l o g2 n] 趟归并。因此自然归并排序将在(n) 的时间内完成排序。而程序1 4 - 3将花费(n l o gn) 的时间。</P>