<>可以运用分而治之方法来解决排序问题,该问题是将n 个元素排成非递减顺序。分而治之方法通常用以下的步骤来进行排序算法:若n 为1,算法终止;否则,将这一元素集合分割成两个或更多个子集合,对每一个子集合分别排序,然后将排好序的子集合归并为一个集合。 b4 i2 t" R0 g " Z- K! c. p# P; ~0 _假设仅将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)的递归算法。0 C. r# `, H0 j7 \3 P" I
, J+ p. |" w8 X+ L7 u假如用冒泡过程(见程序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)。 . D \& S* B: l" g: R0 s 1 I0 v/ Y6 z' ]上述分割方案将n 个元素分成两个极不平衡的集合A和B。A有n- 1个元素,而B仅含一个元素。下面来看一看采用平衡分割法会发生什么情况: A集合中含有n/k 个元素,B中包含其余的元素。递归地使用分而治之方法对A和B进行排序。然后采用一个被称之为归并( m e rg e)的过程,将已排好序的A和B合并成一个集合。 3 ^( i: c0 W# T7 e) S8 O$ w* }# h' S! h; R! j
例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 ]。当这两个排好序的序列被归并后,即可得所需要的排序序列。 ) M- s; a, V# o6 E / n; }. z+ t" S, o R图2 - 6给出了分而治之排序算法的伪代码。算法中子集合的数目为2,A中含有n/k个元素。1 |6 n( e; X8 t1 B
0 U: }2 Q9 \( R8 Y/ s$ X
template<CLASS T> 3 {3 J, S3 r( X: k; [) T4 S' L9 g) c/ A# E( @/ r' x4 g$ p3 V, F
void sort( T E, int n) 1 Y; j+ h1 V; \9 ]) M! b. {5 I5 b% v8 ?9 f
{ / /对E中的n 个元素进行排序, k为全局变量 1 D& I, E" N q$ S+ H8 X 1 z3 p8 @ P6 ~ v( }0 u" ?. E \if (n >= k) {1 ?) v+ Z1 T( O( O
0 U# y+ E1 b" u8 e Fi = n/k;- V& A) O3 }& Y; z: c
2 Z- I7 |. \" |3 X7 Y5 g0 d" l. R9 O
j = n-i; 2 k0 p( ]- O$ F d0 o , n! ~& [1 V& i, t* A令A 包含E中的前i 个元素 . U. D$ m. O- \/ C* R ; W% D( D- J6 c" ]1 K8 r令B 包含E中余下的j 个元素! |- t; V/ {# C9 }1 h3 `
1 K5 G1 K3 \ s* fs o r t ( A , i ) ;! O! Y- i: K5 ?6 x
% `" {: e: U6 Z. _s o r t ( B , j ) ;/ E9 z3 M+ q6 ~& S0 r g
6 h. a; G/ L1 i) o1 qm e rge(A,B,E,i,j,); //把A 和B 合并到E( X1 d+ _' {* V. D6 y9 t* q$ Q6 ~0 N
% v# P' N+ q9 j8 `: \8 a" Iwhile (s < n) {. J7 ^# z" U2 u8 [% Y" E; z2 d9 t
; M4 ^ ~: x' e. o7 D I3 OMergePass(a, b, s, n); // 从a归并到b3 Y, g/ P2 H( D/ e
0 O, u& {( A9 _
s += s;0 F- Q& U0 G2 J
' S2 ?$ M, ~. I, ^
MergePass(b, a, s, n); // 从b 归并到a3 w6 @; U4 e; x) j9 c; D
! S% q# j; Z& k7 Q! ?7 V- o
s += s; % M `9 n$ }* U, d% d4 o5 N8 N" ?( L6 n! m+ \
} ?9 w) Y7 r2 t* |9 K2 n0 o+ V7 p : U6 r+ [% Z @4 x) m2 }2 q% p! B}. p$ B3 B3 W w4 R$ t
& y+ ?% y7 e$ b/ M# F$ f: z4 a为了完成排序代码,首先需要完成函数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定义一个操作符< =。如果需要排序的数据类型是用户自定义类型,则必须重载操作符< =。这种设计方法允许我们按元素的任一个域进行排序。重载操作符< =的目的是用来比较需要排序的域。4 d4 D' r0 |& i' a( ] ^* {: I
3 E) S, W g1 E程序14-4 MergePass函数 * H Z4 C6 N. M P5 [ % m( E2 C H9 P: T% A/ ytemplate<CLASS T> 0 I7 t, [. h: n% Y; F, U$ |. o: L. x) ?( I7 o
void MergePass(T x[], T y[], int s, int n), V4 O9 J# x4 i2 l; {: }
0 I6 ?+ R" z" e# s% C! r{// 归并大小为s的相邻段 , t3 G5 v( {* t/ s. _1 e5 n8 `5 L4 ^
int i = 0;; m9 ^( i! d( K) H% B8 u
+ `( P0 \. {7 r) ewhile (i <= n - 2 * s) { ! E8 U% d- b; G/ z3 B; E5 Q9 ~8 O! S4 m7 k! z2 w% x) G( P1 o
// 归并两个大小为s的相邻段 $ k$ {8 \" l2 A+ g+ f4 g% O ! r- B. O" m8 |( E/ {3 BMerge(x, y, i, i+s-1, i+2*s-1); # b* j& c0 b% b0 W% r/ k8 Q 7 ?3 [1 N' {$ v1 j9 r" fi = i + 2 * s; 7 Z: H) s' `3 [$ l2 p( ?" M4 K, J9 ]: ~
} ! q" }/ A# h$ v1 p / B. q0 P4 \4 A# o3 X9 S, b// 剩下不足2个元素) e. q4 a) _6 E N
4 n6 |7 s& A+ Z8 p+ c: [
if (i + s < n) Merge(x, y, i, i+s-1, n-1);: }' s- C9 `* \+ h' R9 \3 E i* V
2 Y2 r3 e" ?! I1 M3 _2 O
else for (int j = i; j <= n-1; j++) % W$ H4 n9 _1 H8 L+ ?% t- Q- }$ x9 K1 t, f7 c% z- f
// 把最后一段复制到y. j8 r8 s1 V; \ D. m9 Z+ u5 i
( E" O/ E* O! I* H8 e
y[j] = x[j]; * t# B1 z0 O+ m. f1 G2 `8 _0 K, Y; \9 @0 @ _( Z
}& ~, l) C+ p, L2 ?' U2 T
# t; P6 k Z! a% @% H& s" [2 B程序14-5 Merge函数) B5 Z6 {- l% \) W( y* S
6 _: i1 P- r8 C9 Y5 o" X( qtemplate<CLASS T> 8 h' I& e, ?( p( r w$ U8 ^7 M) [0 b5 Z: m
void Merge(T c[], T d[], int l, int m, int r) 0 L; k# p+ U9 W3 s3 F; D" \ ) e# @0 k' a" o# D7 Q{// 把c[l:m]] 和c[m:r] 归并到d [ l : r ] . - G, N- M2 o# A6 g ' E* o" v$ |% L2 E7 mint i = l, // 第一段的游标 * a2 S2 S. M) U/ F# x/ g+ T7 H; z/ w0 c6 [/ i
j = m+1, // 第二段的游标 & Q! ^1 m/ T9 d : e) J0 R2 r8 N$ Q1 ~k = l; // 结果的游标 0 \. T A/ j( i ! W/ r, b0 }, q. I D$ Q8 q1 t3 h1 {/ /只要在段中存在i和j,则不断进行归并 1 o7 }9 ^: W3 K ( b) b S$ q* V) {/ W) S) Ywhile ((i <= m) && (j <= r))% r* q+ l8 ]; k7 t: p0 G3 O