数学建模社区-数学中国

标题: 选择排序 [打印本页]

作者: 韩冰    时间: 2004-10-4 05:17
标题: 选择排序
<>对于给定的n 个元素的数组a [ 0 : n - 1 ],要求从中找出第k小的元素。当a [ 0 : n - 1 ]被排序时,该元素就是a [ k - 1 ]。假设n = 8,每个元素有两个域k e y和I D,其中k e y是一个整数,I D是一个字符。假设这8个元素为[ ( 1 2 ,a),( 4 ,b),( 5 ,c),( 4 ,d),( 5 ,e),( 1 0 ,f),( 2 ,g),( 2 0 ,h)], 排序后得到数组[ ( 2 ,g),( 4 ,d),( 4 ,b),( 5 ,c),( 5 ,e),( 1 0 ,f),( 1 2 ,a),( 2 0 ,h) ]。如果k = 1,返回I D为g 的元素;如果k = 8,返回I D为h 的元素;如果k = 6,返回是I D为f 的元素;如果k = 2,返回I D为d 的元素。实际上,对最后一种情况,所得到的结果可能不唯一,因为排序过程中既可能将I D为d 的元素排在a [ 1 ],也可能将I D为b 的元素排在a [ 1 ],原因是它们具有相同大小的k e y,因而两个元素中的任何一个都有可能被返回。但是无论如何,如果一个元素在k = 2时被返回,另一个就必须在k = 3时被返回。
3 X$ b5 D9 t  n/ o7 a4 y0 m7 F: e& u
选择问题的一个应用就是寻找中值元素,此时k = [n / 2 ]。中值是一个很有用的统计量,例如中间工资,中间年龄,中间重量。其他k值也是有用的。例如,通过寻找第n / 4 , n / 2和3 n / 4这三个元素,可将人口划分为4份。
# N3 B: E% r: t2 N
! U! V, z4 T( u! [2 g9 L& y$ e4 Y/ E选择问题可在O ( n l o g n )时间内解决,方法是首先对这n个元素进行排序(如使用堆排序式或归并排序),然后取出a [ k - 1 ]中的元素。若使用快速排序(如图1 4 - 11所示),可以获得更好的平均性能,尽管该算法有一个比较差的渐近复杂性O( n2 )。  T  g( |& M: a4 W5 `

# t" D. E  p( X可以通过修写程序1 4 - 6来解决选择问题。如果在执行两个w h i l e循环后支点元素a [ l ]被交换到a [ j ] ,那么a [ l ]是a [ l : j ]中的第j - l + 1个元素。如果要寻找的第k 个元素在a [ l : r ]中,并且j - l + 1等于k,则答案就是a [ l ];如果j - l + 1 &lt; k,那么寻找的元素是r i g h t中的第k - j + l - 1个元素,否则要寻找的元素是left 中的第k个元素。因此,只需进行0次或1次递归调用。新代码见程序1 4 - 7。S e l e c t中的递归调用可用f o r或w h i l e循环来替代(练习2 5)。
* V4 w+ l- b# n5 q: e/ F, i* g, z; Z2 Q' P- D$ Q
程序14-7 寻找第k 个元素
& |& S, b* i: y
+ ^# Z4 a) b. i2 s/ utemplate<CLASS T>' f4 }9 `' x- t. ?8 E& o3 t
9 j6 d1 ]" O/ E7 q
T Select(T a[], int n, int k)1 Z1 M6 S7 _( |  R- t. {; d

+ W' E. M. M+ ]+ j{// 返回a [ 0 : n - 1 ]中第k小的元素* s! T9 h- P6 q6 {! [4 h

, M0 e) H( p2 t6 N7 g" s// 假定a[n] 是一个伪最大元素6 ~6 n# F+ F& n

8 k1 X7 \# ?9 f4 ~  L2 q, kif (k &lt; 1 || k &gt; n) throw OutOfBounds();
! H% f, i' F) q: o. U6 B' U7 w) d# n" `5 _2 }. Y) q
return select(a, 0, n-1, k);
6 t% N5 d6 T8 U
  a3 ~9 q0 L* o1 ?+ @% f}6 e8 u/ e$ w) i( Y7 `9 @" O4 i
$ y( x' g) ^5 M4 b# o
template<CLASS T>( C$ w6 ]9 ^* R+ q
: Y1 k2 v$ w+ B9 v9 f" ?
T select(T a[], int l, int r, int k)% z' S7 ~* Y4 `8 Q
! n& O# V$ g+ c  X
{// 在a [ l : r ]中选择第k小的元素
4 Z4 v0 I& e  F7 V
2 Y" ~1 M- j$ Q* \4 V7 u! nif (l &gt;= r) return a[l];; `  r) v+ k0 E0 ]/ Z) _  ^

+ v) J) \4 H0 J( lint i = l, // 从左至右的游标8 {9 D& h+ E! m0 m; O2 W& h
; D* k5 u  M% i8 C
j = r + 1; // 从右到左的游标$ {; K3 X6 j- B7 q! }% T1 G
  Y' k9 h" q+ D1 g8 J
T pivot = a[l];4 D2 ]% [! ]: ^# W* P0 F3 @

6 b# [/ E# b9 m& f7 l# E2 h// 把左侧&gt;= pivot的元素与右侧&lt;= pivot 的元素进行交换3 I) J- g/ ~  X0 h8 i) V* h( I0 x# B" j

3 U) I- x. k0 L! W, awhile (true) {4 s( H6 y9 A, |0 N- W

1 C' W8 }  R* A& B5 S; Ido {// 在左侧寻找&gt;= pivot 的元素
; L1 v) \0 T; G; T: F4 I  Y$ @& i1 \7 w0 t; ]
i = i + 1;
- e& T" c9 A; S' a' N
! i* \4 \9 M( k' l} while (a &lt; pivot);" m% ^& c* k5 g( {! i2 }: B. [

+ m$ x) q4 f+ a, Y) ]- f  qdo {// 在右侧寻找&lt;= pivot 的元素- F5 V6 D, }& Q) C0 y1 I) {0 r3 O( B

3 P( o, J$ G6 q, X5 A5 r; _j = j - 1;
( J8 H# j2 [& G$ E+ W
3 X3 h  v3 u# m. J7 \0 z/ E} while (a[j] &gt; pivot);
4 o% h- [% \3 r9 @3 c
$ _, Y  W0 V: M" Sif (i &gt;= j) break; // 未发现交换对象
  B8 s* b1 z' U0 |+ n6 H1 H  Y" K+ o
: c( p8 e9 H5 q! }3 b5 U4 P- vSwap(a, a[j]);2 ]' f$ o$ s; V# t

$ x3 T) a. Q* T4 F}
8 G0 ^, m$ a" H& ~0 [" b
2 V( v9 x% E# P+ {if (j - l + 1 == k) return pivot;6 |0 D' Q( f$ b6 u1 B$ l4 z* P# H

1 L, R4 {2 u/ Z& W! [8 K// 设置p i v o t8 G1 H. y4 |! A+ r) p6 r. k6 |- p

! z. W* L3 W: f! q2 qa[l] = a[j];
& `7 e6 d  o5 a9 F7 n9 |
8 F% S3 O3 M& V  |9 Ia[j] = pivot;( I  L, T1 u  y& m/ a3 o$ B" P3 b
8 R! u- \3 l2 @6 s- M! ~7 l7 y
// 对一个段进行递归调用
  A) t" n1 L& X0 j5 i5 E' r+ [7 D8 }
if (j - l + 1 &lt; k)
+ q7 O$ R7 o- V# v& b3 x
, L: l& Z( k3 u( Q' {* t' c+ S- nreturn select(a, j+1, r, k-j+l-1);4 \; m6 y0 F/ t4 r( L) N7 n2 q

0 j2 M& v8 o& `; W' W; A; ~else return select(a, l, j-1, k);
* A' t$ M2 \' q" o0 J
, G* u) a$ h7 h  y7 k}
" G8 [$ M# m- J; A+ ^1 n- [, H% Z7 D, l1 ]( N2 p7 e
程序1 4 - 7在最坏情况下的复杂性是( n2 ),此时left 总是为空,而且第k个元素总是位于r i g h t.8 f% x5 U, B, Z. M
* R; H, Z# X) N% j$ r( y) W, S5 z
如果假定n 是2的幂,则可以取消公式(2 - 1 0)中的向下取整操作符。通过使用迭代方法,可以得到t (n) = (n)。若仔细地选择支点元素,则最坏情况下的时间开销也可以变成(n)。一种选择支点元素的方法是使用“中间的中间( m e d i a n - o f - m e d i a n)”规则,该规则首先将数组a中的n 个元素分成n/r 组,r 为某一整常数,除了最后一组外,每组都有r 个元素。然后通过在每组中对r 个元素进行排序来寻找每组中位于中间位置的元素。最后根据所得到的n/r 个中间元素,递归使用选择算法,求得所需要的支点元素。! {+ C; J* g6 @; w8 z

8 N2 F7 x4 p6 R  _: n$ d' i例2-6 [中间的中间] 考察如下情形:r=5, n=27, 并且a= [ 2,6,8,1,4,1 0,2 0,6,2 2,11,9,8,4,3,7,8,1 6,11,1 0,8,2,1 4,1 5,1,1 2,5,4 ]。这2 7个元素可以被分为6组[ 2 , 6 , 8 , 1 , 4 ],[ 1 0 , 2 0 , 6 , 2 2 , 11 ],[ 9 , 8 , 4 , 3 , 7 ],[ 8 , 1 6 , 11 , 1 0 , 8 ],[ 2 , 1 4 , 1 5 , 1 , 1 2 ]和[ 5 , 4 ],每组的中间元素分别为4 , 11 , 7 , 1 0 , 1 2和4。[ 4 , 11 , 7 , 1 0 , 1 2 , 4 ]的中间元素为7。这个中间元素7被取为支点元素。由此可以得到l e ft= [ 2 , 6 , 1 , 4 , 6 , 4 , 3 , 2 , 1 , 5 , 4 ],m i d d l e= [ 7 ] ,r i g h t= [ 8 , 1 0 , 2 0 , 2 2 , 11 , 9 , 8 , 8 , 1 6 , 11 , 1 0 , 8 , 1 4 , 1 5 , 1 2 ]。+ p" T# K* K' W* @
, t8 g& c* ~" a( x
如果要寻找第k个元素且k&lt; 1 2,则仅仅需要在l e f t中寻找;如果k= 1 2,则要找的元素就是支点元素;如果k&gt; 1 2,则需要检查r i g h t中的1 5个元素。在最后一种情况下,需在r i g h t中寻找第(k- 1 2 )个元素。4 v/ Q2 [# o9 A7 l

, e2 q$ e+ H) l: u) L定理2-2 当按“中间的中间”规则选取支点元素时,以下结论为真:
4 y% ?' B/ h$ g) z. `
0 j- Q5 }# s! ]* X' j1) 若r=9, 那么当n≥9 0时,有m a x { |l e f e|, |r i g h t| }≤7n / 8。
" j- s# A# E' N% d1 p
1 x. n' D: r8 g- e. u2) 若r= 5,且a 中所有元素都不同,那么当n≥2 4时,有max{| left |, | right | }≤3n/ 4。' }( _" q6 k. a3 c, n; n6 T

- j8 ^" n8 a7 n* N* X! Q证明这个定理的证明留作练习2 3。
5 t- Z- c5 X# w$ v7 Y/ U* e2 j% `/ f1 q( [+ o9 [! }: S
根据定理2 - 2和程序1 4 - 7可知,如果采用“中间的中间”规则并取r= 9,则用于寻找第k个元素的时间t (n)可按如下递归公式来计算:
7 \7 X( G; r# u- Y. h! L* J) P& k. u5 C. e: T& q, o
在上述递归公式中,假设当n<9 0时使用复杂性为nl o gn的求解算法,当n≥9 0时,采用“中间的中间”规则进行分而治之求解。利用归纳法可以证明,当n≥1时有t (n)≤7 2cn (练习2 4 )。: L' ]# |4 x! u9 ]# x
: E$ K) q  Q- X" i
当元素互不相同时,可以使用r= 5来得到线性时间性能。</P>




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5