QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3397|回复: 0
打印 上一主题 下一主题

选择排序

[复制链接]
字体大小: 正常 放大
韩冰        

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-4 05:17 |只看该作者 |正序浏览
|招呼Ta 关注Ta
<>对于给定的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时被返回。
! n3 @: L/ T, X( L# X) g! Z1 i+ U; [6 E3 s% o
选择问题的一个应用就是寻找中值元素,此时k = [n / 2 ]。中值是一个很有用的统计量,例如中间工资,中间年龄,中间重量。其他k值也是有用的。例如,通过寻找第n / 4 , n / 2和3 n / 4这三个元素,可将人口划分为4份。: i; G. o. e; `" P0 |

4 s4 o  L5 A9 f& w. K- V$ _选择问题可在O ( n l o g n )时间内解决,方法是首先对这n个元素进行排序(如使用堆排序式或归并排序),然后取出a [ k - 1 ]中的元素。若使用快速排序(如图1 4 - 11所示),可以获得更好的平均性能,尽管该算法有一个比较差的渐近复杂性O( n2 )。
9 R9 ?! u! F2 t# m. M" W0 B; j, O3 e" q
可以通过修写程序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)。
, @  U8 C$ n* r
4 j0 V1 L* q) ?9 _; Z6 X程序14-7 寻找第k 个元素
- L' q! m$ Z8 ~/ s5 W6 x% _2 d( S, C6 \; i
template<CLASS T>
% E$ @2 `2 J/ o5 `: g1 [& q" U7 j7 I( U+ b) {$ Y/ A' G
T Select(T a[], int n, int k)  F3 q! N0 d8 `. s) A& @; h

8 i% m$ Z. A6 V( A& }{// 返回a [ 0 : n - 1 ]中第k小的元素$ m0 D/ {& x6 t. C

* }0 b+ H# c0 G; y2 C1 g, j// 假定a[n] 是一个伪最大元素& V. U% I$ h# P3 h' D; f
& S5 k2 ^' ?$ j) I7 J% v
if (k &lt; 1 || k &gt; n) throw OutOfBounds();- c( i, i% Z4 v1 Z6 \: ^& E

3 }& Y6 t2 l" f: Y: B, \return select(a, 0, n-1, k);, R+ z! E  M7 o8 |
" e5 n& @6 I& n( U
}
9 s" S  v( B# z+ i9 s2 |/ L+ d; z$ u4 e4 C: G8 |' a9 J
template<CLASS T>" ]( C' E$ i* r
) e- a  U$ e, O2 c6 {) G2 f
T select(T a[], int l, int r, int k)
  C* Z4 J$ T5 O) W: a6 ^) J
0 d5 e, R1 |- V- z$ q{// 在a [ l : r ]中选择第k小的元素. A7 }% B' P3 ^! [* a
5 j$ B$ e, ]2 Y$ J9 ^$ t) l: E
if (l &gt;= r) return a[l];
* t) e5 X4 `  [; Y$ |! K, ~7 }; S( j9 e& r- s* F0 G  K: Y3 ?
int i = l, // 从左至右的游标" c9 j# S  _. t8 K
2 {' B1 t  `) L
j = r + 1; // 从右到左的游标
8 f, s$ V: s" A; s' i3 t6 J5 i# {" z- L5 _$ ?4 q$ B: x
T pivot = a[l];
; B5 a# n6 e+ l+ y) h/ I9 c1 z! g# ^8 [2 R
// 把左侧&gt;= pivot的元素与右侧&lt;= pivot 的元素进行交换* z4 h7 s: f+ q1 c
* K0 E' G" ]+ A0 j% Q5 P
while (true) {3 A+ b1 b/ `6 G8 _8 w

/ G# q* D2 E% G+ {! Sdo {// 在左侧寻找&gt;= pivot 的元素! m! Y) f$ ]) `  {0 Q/ a& Q
: j! y$ K- F2 C
i = i + 1;
6 w9 b  R5 E5 h1 V' G. U8 j# S$ T9 ^/ u9 X
} while (a &lt; pivot);7 _) c7 o3 \3 Z+ e! ?) x% C

$ l* a7 }1 j! h3 l: Z! pdo {// 在右侧寻找&lt;= pivot 的元素
2 ]- _. t5 d: O; `) S# f. G. @7 D# S  L: t0 L
j = j - 1;" w+ {) f/ |, R6 n3 m
+ m0 b. f7 _: M
} while (a[j] &gt; pivot);! W5 q' ]7 C1 L3 a9 I
  p6 J7 W. E# j, W7 E
if (i &gt;= j) break; // 未发现交换对象
; E4 e, ]9 ]# p- G! V1 ^) {+ F4 ~$ \2 p/ Y0 f
Swap(a, a[j]);
! o6 ?, K' [( c6 |% B8 n1 s! l
  O  e0 d) E6 x) d. \+ l}
/ y+ T. ]' ?/ N0 A6 r
& Z, h5 Z+ E# P% _, Eif (j - l + 1 == k) return pivot;
2 j" l; Y, ]' U" Z) o+ j+ f' u' R
$ p. c  {/ {) Q6 a2 M, z// 设置p i v o t
; Y8 ~6 A( ~& ]5 l( g+ s9 S* B6 Y; b
a[l] = a[j];$ X. D" l. i9 o$ I% u- B

( o' ~* G9 o+ f; m8 X& z0 Ja[j] = pivot;
; N: P5 N; M% f+ G; h& X2 q
: f' R# }2 ^$ Z7 |( Y, @, p// 对一个段进行递归调用
. P2 A/ W- D' I' t8 a6 ?! _3 @- a. i* ~  l
if (j - l + 1 &lt; k)
. l& K5 I5 Q+ D7 j1 f4 d$ t1 F2 G+ g) _/ ]0 Q" q9 |
return select(a, j+1, r, k-j+l-1);6 l; C/ _( y! [5 }
# _$ U0 u' ~5 l" X0 M, ^
else return select(a, l, j-1, k);
1 M' z  r- r  q/ H  F( j8 g* I0 o- o
* [# N2 V% J- v  @  P5 S; M2 D% `. Z}' w; S/ ~) Z1 V8 E7 X9 r* ?4 K
' J9 A# d( r6 @2 D9 K1 Z* t9 k
程序1 4 - 7在最坏情况下的复杂性是( n2 ),此时left 总是为空,而且第k个元素总是位于r i g h t.
) v$ C, E" D+ e. n8 e/ u( F$ j7 c. O& j7 |% @4 |: g! K
如果假定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 个中间元素,递归使用选择算法,求得所需要的支点元素。
! L' C2 C1 {4 Z3 l% `- b4 J4 c* e2 |+ C- S
例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 ]。4 ~3 J1 ~5 ?# l$ P% z  s
+ v, B. @. s( H, |6 U" t5 T
如果要寻找第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 )个元素。
  W+ d$ ^/ |% X) K9 _$ V  Y! X
% M) M$ q7 ^2 j% c定理2-2 当按“中间的中间”规则选取支点元素时,以下结论为真:* Y" Q4 d! D  B+ y4 l2 ?

- [3 Z0 F/ @3 Z6 i2 ~1) 若r=9, 那么当n≥9 0时,有m a x { |l e f e|, |r i g h t| }≤7n / 8。0 |+ r5 z& E0 A

, T! b% |& z1 A/ e  Q  }+ N2) 若r= 5,且a 中所有元素都不同,那么当n≥2 4时,有max{| left |, | right | }≤3n/ 4。  h! f6 w& `# G
5 J, D& V' p: s0 c
证明这个定理的证明留作练习2 3。
( V1 g# r) V- j1 y8 Y  H2 v8 ?9 w
, h% {  h1 y! R! {根据定理2 - 2和程序1 4 - 7可知,如果采用“中间的中间”规则并取r= 9,则用于寻找第k个元素的时间t (n)可按如下递归公式来计算:
; }7 N0 T2 \6 g3 R9 T
/ n/ p& r& U  G- D1 v5 [在上述递归公式中,假设当n<9 0时使用复杂性为nl o gn的求解算法,当n≥9 0时,采用“中间的中间”规则进行分而治之求解。利用归纳法可以证明,当n≥1时有t (n)≤7 2cn (练习2 4 )。4 k9 e" _# ]' d; K
) a  V  Y4 Q$ |0 S6 J
当元素互不相同时,可以使用r= 5来得到线性时间性能。</P>
zan
转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
您需要登录后才可以回帖 登录 | 注册地址

qq
收缩
  • 电话咨询

  • 04714969085
fastpost

关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

手机版|Archiver| |繁體中文 手机客户端  

蒙公网安备 15010502000194号

Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

GMT+8, 2026-6-12 02:12 , Processed in 0.419416 second(s), 53 queries .

回顶部