<>可以运用分而治之方法来解决排序问题,该问题是将n 个元素排成非递减顺序。分而治之方法通常用以下的步骤来进行排序算法:若n 为1,算法终止;否则,将这一元素集合分割成两个或更多个子集合,对每一个子集合分别排序,然后将排好序的子集合归并为一个集合。 ) i! w) H' u& s% z2 R0 f: @3 z ! N, @/ w& o& s" r2 N' b: P, V假设仅将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)的递归算法。- E$ V; V+ w, p- b M. z
: F; j6 w, ~% q: o- O
假如用冒泡过程(见程序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)。3 @# a0 e1 l6 H" {1 P
7 s& N1 E6 f2 n- R2 u' {令B 包含E中余下的j 个元素9 V; K+ [3 U% O5 v
" d; B4 `+ Y0 ]+ j8 \/ A3 ^# p4 K: }+ S! R
s o r t ( A , i ) ; * _ D$ \+ q0 O2 h; |; y/ Q) C ! w5 }" J. S0 z7 u' t. X9 \s o r t ( B , j ) ;) S% O G- u: `) G% i
9 R& Y* h3 Q" {; tm e rge(A,B,E,i,j,); //把A 和B 合并到E . V: j5 y; G6 P: \ , g7 R, t0 \: n* \. S. H} " U# m) j1 f/ R ! P, d$ Y0 r' S5 Kelse 使用插入排序算法对E 进行排序 ! p) _/ e6 M( I- j3 T- d" v0 w + R; C. t: w( f} / p6 |' V o( Z, b7 P ; v- m! X& o( I$ p* G9 q( K: d图14-6 分而治之排序算法的伪代码' e* }3 C3 ^3 y% o
% _) |3 E8 a, | L( x2 f' ?8 U9 O+ d! Q- Y( [5 d" U6 A4 ~" E
! G- l9 I8 B( u# @8 g/ Z6 U5 A
从对归并过程的简略描述中,可以明显地看出归并n个元素所需要的时间为O (n)。设t (n)为分而治之排序算法(如图1 4 - 6所示)在最坏情况下所需花费的时间,则有以下递推公式:3 I0 N" o- Y4 ~- L: s* s2 g
; @ ?! Q; B. U$ q @
其中c 和d 为常数。当n / k≈n-n / k 时,t (n) 的值最小。因此当k= 2时,也就是说,当两个子集合所包含的元素个数近似相等时, t (n) 最小,即当所划分的子集合大小接近时,分而治之算法通常具有最佳性能。- [5 B. C+ {% X
; j2 j3 ]5 U9 c可以用迭代方法来计算这一递推方式,结果为t(n)= (nl o gn)。虽然这个结果是在n为2的幂时得到的,但对于所有的n,这一结果也是有效的,因为t(n) 是n 的非递减函数。t(n) =(nl o gn) 给出了归并排序的最好和最坏情况下的复杂性。由于最好和最坏情况下的复杂性是一样的,因此归并排序的平均复杂性为t (n)= (nl o gn)。/ t3 M6 c" `1 p1 j$ M! v
3 _. j4 F) z5 m+ C$ P图2 - 6中k= 2的排序方法被称为归并排序( m e rge sort ),或更精确地说是二路归并排序(two-way merge sort)。下面根据图1 4 - 6中k= 2的情况(归并排序)来编写对n 个元素进行排序的C + +函数。一种最简单的方法就是将元素存储在链表中(即作为类c h a i n的成员(程序3 -8))。在这种情况下,通过移到第n/ 2个节点并打断此链,可将E分成两个大致相等的链表。* O: X- l2 {2 P" P" R, h
+ M4 L/ }7 g# Z 5 E8 y8 v/ `- I- M, |- e- d' O7 E8 W& |% h' A1 ]9 h
template<CLASS T>8 K: i4 c" T6 y
. z5 R. y+ f7 b" J* k
M e rgeSort( T a[], int left, int right)0 D) Q8 ~0 J* C1 T# S6 P9 r5 s
- w' Y j- D! F4 n{ / /对a [ l e f t : r i g h t ]中的元素进行排序3 g( @, J- L: S! Y
6 b1 X+ j5 q4 ?# @+ a1 X3 I: ^1 d
if (left < right) {//至少两个元素% X0 T& z6 j+ ~+ X0 z* l: u4 j
' O( u/ a- A' ^- zint i = (left + right)/2; //中心位置 & \+ e4 ~$ A" z4 i6 x p7 C7 x0 C* O1 F% t( ^4 [
M e rgeSort(a, left, i);& z; j' k! F: k
9 ]4 i5 Q" H3 R: Z- N1 pM e rgeSort(a, i+1, right);/ w* h' V6 m5 J1 O. [# s1 D V* @
, M! V8 p* t4 Q( ^( g
M e rge(a, b, left, i, right); //从a 合并到b7 N: Z7 U; h4 b- s
+ v+ Q2 y4 q6 E9 C( f+ @* I3 @3 _, k
Copy(b, a, left, right); //结果放回a 2 I: M y# U6 j3 a9 ~% u& B2 ]7 m2 _# {, Y+ F2 B$ u* H& X
}3 g% U3 m8 h5 \. H. m: x: y, M. e' D* ~
; @6 M! x! [3 k1 O$ _
}2 c$ z" ]3 a$ h
3 j, I; V r3 L( |& P
图14-7 分而治之排序算法的改进 9 ]1 q8 L1 ^0 x8 A2 h( D3 Z0 h& |