<>分而治之方法还可以用于实现另一种完全不同的排序方法,这种排序法称为快速排序(quick sort)。在这种方法中, n 个元素被分成三段(组):左段l e f t,右段r i g h t和中段m i d d l e。中段仅包含一个元素。左段中各元素都小于等于中段元素,右段中各元素都大于等于中段元素。因此l e f t和r i g h t中的元素可以独立排序,并且不必对l e f t和r i g h t的排序结果进行合并。m i d d l e中的元素被称为支点( p i v o t )。图1 4 - 9中给出了快速排序的伪代码。) A( |% A1 ^; A3 h# |) N' H) Y" I) O( @
+ d( M, q* O' q9 H' i* z1 P8 T) T* M* r6 Y- v. S d- y
/ /使用快速排序方法对a[ 0 :n- 1 ]排序 ' z( D; t' ?9 [! I ) ?- i, |* r3 @8 N' U5 V从a[ 0 :n- 1 ]中选择一个元素作为m i d d l e,该元素为支点 . ?" M3 J7 T* X0 v* y( d! k. A! P3 B3 u7 @3 T3 u0 p0 f, W6 R2 A& [
把余下的元素分割为两段left 和r i g h t,使得l e f t中的元素都小于等于支点,而right 中的元素都大于等于支点 ) Y8 V2 { f8 k6 ^ X8 d/ L* U1 }5 W5 @
递归地使用快速排序方法对left 进行排序; x' h9 E6 D9 H* d" B2 `
( u- T2 w$ T: \9 _( Y
递归地使用快速排序方法对right 进行排序; k% p2 Z k7 ?" {" [6 b+ y
_% h8 p6 W/ r, ]所得结果为l e f t + m i d d l e + r i g h t$ C5 W6 [9 ~ T0 I1 \; `
* E3 y: w' `' z; m0 F( |' S# u3 N图14-9 快速排序的伪代码 2 n+ T+ n( w. k; ]- w ) `) {, o1 l. X; A- e0 ?, s1 C) G% C2 c7 s% _
考察元素序列[ 4 , 8 , 3 , 7 , 1 , 5 , 6 , 2 ]。假设选择元素6作为支点,则6位于m i d d l e;4,3,1,5,2位于l e f t;8,7位于r i g h t。当left 排好序后,所得结果为1,2,3,4,5;当r i g h t排好序后,所得结果为7,8。把right 中的元素放在支点元素之后, l e f t中的元素放在支点元素之前,即可得到最终的结果[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]。7 m2 g! P2 i8 |( w+ M
, \. D: [) w) B; ~
把元素序列划分为l e f t、m i d d l e和r i g h t可以就地进行(见程序1 4 - 6)。在程序1 4 - 6中,支点总是取位置1中的元素。也可以采用其他选择方式来提高排序性能,本章稍后部分将给出这样一种选择。8 M' |- u- h8 s( R- {/ i } ^
7 q# R9 r- |8 [$ y4 S
程序14-6 快速排序0 Q; ^% ? k. A% q8 H
5 E4 J4 e4 R! i
template<CLASS T> ' L% c# M& s: d0 D* j) r6 |& U4 z1 N9 Q$ A% o6 ? v
void QuickSort(T*a, int n) 1 U/ w- O+ k# ]) \' a! g$ U/ _' j6 m5 p9 j9 F# {( j* k: ]
{// 对a[0:n-1] 进行快速排序( J) I8 g2 o( ?* h5 R
/ w( b% R9 k& ^8 u( a) K+ h图2 - 11中分别给出了根据实验所得到的归并排序、堆排序、插入排序、快速排序的平均时间。对于每一个不同的n, 都随机产生了至少1 0 0组整数。随机整数的产生是通过反复调用s t d l i b . h库中的r a n d o m函数来实现的。如果对一组整数进行排序的时间少于1 0个时钟滴答,则继续对其他组整数进行排序,直到所用的时间不低于1 0个时钟滴答。在图2 - 11中的数据包含产生随机整数的时间。对于每一个n,在各种排序法中用于产生随机整数及其他开销的时间是相同的。因此,图2 - 11中的数据对于比较各种排序算法是很有用的。 9 z" b4 l5 S2 [ ' q" y4 ?- J3 F4 n/ P对于足够大的n,快速排序算法要比其他算法效率更高。从图中可以看到快速排序曲线与插入排序曲线的交点横坐标比2 0略小,可通过实验来确定这个交点横坐标的精确值。可以分别用n = 1 5 , 1 6 , 1 7 , 1 8 , 1 9进行实验,以寻找精确的交点。令精确的交点横坐标为nBr e a k。当n≤nBreak 时,插入排序的平均性能最佳。当n>nBreak 时,快速排序性能最佳。当n>nBreak 时,把插入排序与快速排序组合为一个排序函数,可以提高快速排序的性能,实现方法是把程序1 4 - 6中的以下语句:0 j0 \9 { G" e3 ^: q
, Q) i' E+ f' h6 Q# L$ P
if(l >= r)r e t u r n ; L$ c" m! x3 R, W4 s* w% y0 g9 B5 h; h) h6 a7 P
替换为% J8 u3 ^) H: m- D
/ Q$ z% [' J, {: V4 T: q/ t8 [3 Z; v% yif (r-1<NBREAK) {InsertionSort(a,l,r); return;}5 Z+ \' e9 X! v; r- h b
: Q- ~) E4 X" H6 P; e9 L( t! h
这里I n s e r t i o n S o r t ( a , l , r )用来对a [ 1 : r ]进行插入排序。测量修改后的快速排序算法的性能留作练习(练习2 0)。用更小的值替换n B r e a k有可能使性能进一步提高(见练习2 0)。9 i3 m: `! i. E' M6 ?
3 C+ G% [1 S5 b: i3 E
大多数实验表明,当n>c时(c为某一常数),在最坏情况下归并排序的性能也是最佳的。而当n≤c时,在最坏情况下插入排序的性能最佳。通过将插入排序与归并排序混合使用,可以提高归并排序的性能(练习2 1)。</P>