< align=center><B></B> </P>* o- a2 r% q" M: N
<>分而治之方法还可以用于实现另一种完全不同的排序方法,这种排序法称为快速排序(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中给出了快速排序的伪代码。$ w. I% l" r' u7 N, o2 p
) M. ~$ H) u2 M6 X* ^ e! f
' h' ^* y' @% f4 j, |( O
/ /使用快速排序方法对a[ 0 :n- 1 ]排序 * ]* ?" n5 t+ L. F 6 r/ F9 q/ u9 Z7 J+ g; K从a[ 0 :n- 1 ]中选择一个元素作为m i d d l e,该元素为支点7 s1 e# ?: [9 j5 h
+ u8 \1 n. A* V' M
把余下的元素分割为两段left 和r i g h t,使得l e f t中的元素都小于等于支点,而right 中的元素都大于等于支点 2 Y0 ^" r. S& k4 a2 j6 O0 u5 R3 U: O) i: {4 I7 a9 e
递归地使用快速排序方法对left 进行排序. ~" F+ c5 b5 x" g! k/ g* u, P
- h2 A3 n" Q3 |
递归地使用快速排序方法对right 进行排序# x+ z+ c" M& F: J1 S9 W& A: o
7 f0 T* y- l' Z& C& k% ?所得结果为l e f t + m i d d l e + r i g h t7 K: v* z# `9 V& t6 [. q. Q/ Z
; e, e2 @# O! _+ ]图14-9 快速排序的伪代码4 e' | _. V0 r6 s% e( Q- e [8 l
4 q. `' w! b2 d/ N' W6 j T
" _- P! {0 t7 f; U9 c9 W* v/ G考察元素序列[ 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 ]。 # W; g/ W8 [2 y; T" l$ d7 g; K3 S5 J q7 Q) ?
把元素序列划分为l e f t、m i d d l e和r i g h t可以就地进行(见程序1 4 - 6)。在程序1 4 - 6中,支点总是取位置1中的元素。也可以采用其他选择方式来提高排序性能,本章稍后部分将给出这样一种选择。5 G6 K: X" Z$ F# q1 I0 x
. m& \* J$ A0 [2 P4 U) w! c5 o
程序14-6 快速排序: B2 ]! k5 u1 J4 ]3 R% `# g) v
% C1 v9 T& q3 Z/ j4 S+ n) Qtemplate<CLASS T> + F2 q( K# Z. C6 _ . \) r: M8 f' @$ q; h$ G' C3 rvoid QuickSort(T*a, int n) % w+ g% ?/ J: c- q7 W5 C ! U4 n4 P; e" z6 R/ L4 @: |{// 对a[0:n-1] 进行快速排序 ( P& d0 j5 B$ s( o; F' l* x* z9 e% Z
{// 要求a[n] 必需有最大关键值' R3 B E; P3 H5 R- ^5 K
) O. D) j k" d" g+ g1 q& a2 H/ AquickSort(a, 0, n-1);$ |# k; q9 l' N
- o, d# p9 B4 t6 @6 b R# B, u* z9 T% N& t选择排序n2 n2) A9 U" u& y0 J) F9 R/ g+ \
1 [% i+ ~8 A; _$ _
堆排序nl o gn nl o gn3 H2 G$ m5 `( p% b6 z" @+ ^
5 `: M2 G: X6 ~9 B E/ j8 L2 }; ?4 c
归并排序nl o gn nl o gn ! S- I2 ^: Q" E6 Q* q, F , {8 ?3 |) A3 g7 w快速排序n2 nl o gn " ?& v. }+ P- t3 W% U- t; U0 W! p' {. B+ o n
图14-10 各种排序算法的比较) q w. K: F% V' E3 O9 e. u4 Y7 f
; [9 a% s: ~9 s' t) p2 C
" s u, d1 c9 C: A6 b" S' _% F
中值快速排序( median-of-three quick sort)是程序1 4 - 6的一种变化,这种算法有更好的平均性能。注意到在程序1 4 - 6中总是选择a [ 1 ]做为支点,而在这种快速排序算法中,可以不必使用a [ 1 ]做为支点,而是取{a[1],a[(1+r)/2],a[r]} 中大小居中的那个元素作为支点。例如,假如有三个元素,大小分别为5,9,7,那么取7为支点。为了实现中值快速排序算法,一种最简单的方式就是首先选出中值元素并与a[1] 进行交换,然后利用程序1 4 - 6完成排序。如果a [ r ]是被选出的中值元素,那么将a[1] 与a[r] 进行交换,然后将a [ 1 ](即原来的a [ r ])赋值给程序1 4 - 6中的变量p i v o t,之后继续执行程序1 4 - 6中的其余代码。 & V6 m" K$ R. \ 7 I: e, e& U* @图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中的数据对于比较各种排序算法是很有用的。 ) e# M( \) O$ x/ B7 A ; O. m* O* u% z5 T( }; P/ d) ?对于足够大的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中的以下语句: 2 |7 l4 r/ {; m) ^- M ; B) G, ~* u$ p9 D" I7 cif(l >= r)r e t u r n ; 5 }$ ]' |6 B u& ? , ]- A* h6 U3 ^: _* G替换为 , @1 |* r. I0 v5 j: l0 | ! j/ [9 E. W# r0 T6 n2 G1 Bif (r-1<NBREAK) {InsertionSort(a,l,r); return;}& \5 s' Q! F, ~. l! c$ {; Q
5 A* l( t$ T; p, b. 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)。- L! N2 j/ E' J% G' f* p
- T* ?3 e: q0 }
大多数实验表明,当n>c时(c为某一常数),在最坏情况下归并排序的性能也是最佳的。而当n≤c时,在最坏情况下插入排序的性能最佳。通过将插入排序与归并排序混合使用,可以提高归并排序的性能(练习2 1)。</P>