< align=center><B></B> </P>7 G3 O- q% E) ~. L2 K
<>分而治之方法还可以用于实现另一种完全不同的排序方法,这种排序法称为快速排序(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中给出了快速排序的伪代码。 , P! A0 E$ k9 \. \$ [5 @/ }% V+ [2 B; y$ ?) R5 T
7 h8 V6 U1 q5 Z# t/ /使用快速排序方法对a[ 0 :n- 1 ]排序 ; T5 n7 j1 `7 J9 J ) @' g2 G0 z4 x, T* l/ Z3 }从a[ 0 :n- 1 ]中选择一个元素作为m i d d l e,该元素为支点4 t( Z) V5 ~8 y4 r, `; K
0 {. Z. M* n: e+ S
把余下的元素分割为两段left 和r i g h t,使得l e f t中的元素都小于等于支点,而right 中的元素都大于等于支点2 Y: p: C; Y7 P" Q0 n) a& H3 @, z
( E/ r0 j1 }/ K' J3 I
递归地使用快速排序方法对left 进行排序 # c4 R: V, U6 F9 j* y$ n 8 P9 J1 w, w7 Y" f% j% J+ ]递归地使用快速排序方法对right 进行排序 0 I+ ]8 U3 i, E1 v& { / I7 b3 @9 T" z$ |所得结果为l e f t + m i d d l e + r i g h t( J. B9 [$ |- I" r
- e4 o+ v: _7 |& s图14-9 快速排序的伪代码0 q" }% H) D: [: O D( Z# H/ L
9 n8 _3 ?% b- R7 Q8 l4 p3 _" f6 x
3 m: V0 Q9 o* f1 [
考察元素序列[ 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 ]。0 F4 i" C/ t3 Z+ ~) c( _
0 C) I& O* K/ d ]+ K* @- [把元素序列划分为l e f t、m i d d l e和r i g h t可以就地进行(见程序1 4 - 6)。在程序1 4 - 6中,支点总是取位置1中的元素。也可以采用其他选择方式来提高排序性能,本章稍后部分将给出这样一种选择。 / v( x) P8 o9 W( K$ R7 b ; X4 U: X4 X0 @9 L5 F: l程序14-6 快速排序 : S- B. X7 K7 R' @. i , `2 Z' h5 p& [/ H7 F2 _% H: Atemplate<CLASS T> ) O4 H( ^5 Z: u: P# C % v! ~3 H- A. Y' M% O" _void QuickSort(T*a, int n)7 V' F6 v3 B! a. U4 a7 C
7 {- c6 Q9 g, e. t8 q* {+ e( l
{// 对a[0:n-1] 进行快速排序/ }: b" w' j& J) s
& }9 }7 O9 r( s7 m6 L l, o. O{// 要求a[n] 必需有最大关键值 * g5 u1 B4 U: b2 M9 W! I# w8 W% P; d- d4 k3 E4 A9 o
quickSort(a, 0, n-1);1 J, ]6 z( E8 F1 x8 p; Y2 e
5 A2 T+ q" `6 `" l( t5 p; |
template<CLASS T> 1 X5 i& X" j. r4 }# ~# o0 y 0 z% |' T& _" T2 N- Hvoid quickSort(T a[], int l, int r)! ?9 ?, U5 W. L5 m5 z- _/ ], h9 i. t" o) H
% m$ ^% E. r3 t/ X. l{// 排序a [ l : r ], a[r+1] 有大值 % }# Z! M, J v) K. z& @9 H/ V
if (l >= r) return;, {1 _8 g. P7 c7 R. _! r4 Z. {
4 D' P# @, G% B+ W) ^: @" sint i = l, // 从左至右的游标 / c9 ]' o2 H {) s# V9 G" Z1 ]+ t: |! H( Y( S- Q( W
j = r + 1; // 从右到左的游标5 t5 `5 x9 |# U% t% }) S
- u' _2 |. C0 f2 b) ?1 p9 t9 i+ fT pivot = a[l];$ w% N# C" ]2 I: i4 @
% E( d8 ~+ X, W8 F* f, t
// 把左侧>= pivot的元素与右侧<= pivot 的元素进行交换! L3 O* l/ p4 m' _: e
! |( j) d' }* S$ h4 u
while (true) {9 z( Z" ^" Q& f1 K
. e! q& }- S. i7 o9 }do {// 在左侧寻找>= pivot 的元素/ _# ~7 h( c0 r6 ?" e
y, l% K( @8 c- @8 F5 c4 s4 _
i = i + 1;9 \. s- V d& q! y+ e* R' O
$ _& r, R! |; G1 S
} while (a < pivot);" w3 A, u/ ]! A
' t8 x1 z# l4 v
do {// 在右侧寻找<= pivot 的元素& ^& u5 x3 t& U2 c
4 O7 q$ z* Y' k8 c' {( Q+ H5 oj = j - 1;( h5 n7 \$ [' A9 U
0 h& G/ R( L; o' H; u: W% v
} while (a[j] > pivot); 4 r4 Y$ |; S: e! L2 ~6 ^: W. Z1 a# `! ?5 M6 o2 G& J
if (i >= j) break; // 未发现交换对象& A, e/ P& ~+ C, y2 B6 p9 w8 J
' S3 Q/ Z' a0 \( i; \* V
Swap(a, a[j]); 5 \# Z0 u; F0 ]( _ 6 B2 v% m Y3 v" V4 n. K7 g; l}' h8 Y+ g0 f4 d# M5 ^
5 d& l) f; }' h+ e/ a// 设置p i v o t! p& f+ `6 N* u. i
; w# \( x8 Q& d0 d& z2 b! ma[l] = a[j];$ T$ z& @. p J# b
$ C) g8 V( c0 E4 Z( a; Q) g
a[j] = pivot;" ~. p- F, L9 t) Z0 S6 y% L+ J1 U9 d
! C0 [& G0 b7 R% B8 {: AquickSort(a, l, j-1); // 对左段排序 : e0 ~5 Y# E+ X g5 a ! S. U+ Z! c1 Z5 D) O0 UquickSort(a, j+1, r); // 对右段排序0 b. m0 R" y5 w" I( g
2 a) p) H; l' `
}; z+ Z0 E3 _8 K6 S
% h* ~1 I K U" J9 i
若把程序1 4 - 6中d o - w h i l e条件内的<号和>号分别修改为< =和> =,程序1 4 - 6仍然正确。实验结果表明使用程序1 4 - 6的快速排序代码可以得到比较好的平均性能。为了消除程序中的递归,必须引入堆栈。不过,消除最后一个递归调用不须使用堆栈。消除递归调用的工作留作练习(练习1 3)。程序1 4 - 6所需要的递归栈空间为O (n)。若使用堆栈来模拟递归,则可以把这个空间减少为O ( l o gn)。在模拟过程中,首先对left 和right 中较小者进行排序,把较大者的边界放入堆栈中。在最坏情况下l e f t总是为空,快速排序所需的计算时间为(n2 )。在最好情况下, l e f t和r i g h t中的元素数目大致相同,快速排序的复杂性为(nl o gn)。令人吃惊的是,快速排序的平均复杂性也是(nl o gn)。 ! |3 o* M6 J1 @8 e. H- o4 j0 k0 E/ L5 J! o7 S
定理2-1 快速排序的平均复杂性为(nl o gn)。4 ]& F8 f% c* T( O; e1 }
4 y9 s. z8 H' ~, x6 ~
证明用t (n) 代表对含有n 个元素的数组进行排序的平均时间。当n≤1时,t (n)≤d,d为某一常数。当n <1时,用s 表示左段所含元素的个数。由于在中段中有一个支点元素,因此右段中元素的个数为n-s- 1。所以左段和右段的平均排序时间分别为t (s), t (n-s- 1 )。分割数组中元素所需要的时间用cn 表示,其中c 是一个常数。因为s 有同等机会取0 ~n- 1中的任何一个值. 3 y9 B+ J8 V6 P5 n. g3 \7 ~! r* Q( v- h h
如对(2 - 8)式中的n 使用归纳法,可得到t (n)≤kn l o ge n,其中n> 1且k=2(c+d),e~2 . 7 1 8为自然对数的基底。在归纳开始时首先验证n= 2时公式的正确性。根据公式( 1 4 - 8),可以得到t( 2 )≤2c+ 2d≤k nl o ge 2。在归纳假设部分,假定t(n)≤kn l o ge n(当2≤n<m 时,m 是任意一个比2大的整数=.9 N- M! P4 l- K: b