QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3475|回复: 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时被返回。4 i5 q9 ~7 C9 v  ~, l

0 Z1 `6 w0 c4 [  q) I选择问题的一个应用就是寻找中值元素,此时k = [n / 2 ]。中值是一个很有用的统计量,例如中间工资,中间年龄,中间重量。其他k值也是有用的。例如,通过寻找第n / 4 , n / 2和3 n / 4这三个元素,可将人口划分为4份。9 \. X7 s% H; J6 N' q3 j7 v3 x

, y0 `2 r2 i$ o6 x选择问题可在O ( n l o g n )时间内解决,方法是首先对这n个元素进行排序(如使用堆排序式或归并排序),然后取出a [ k - 1 ]中的元素。若使用快速排序(如图1 4 - 11所示),可以获得更好的平均性能,尽管该算法有一个比较差的渐近复杂性O( n2 )。4 u& Y% @7 h& p  ^
7 t( h. s% k- z2 n
可以通过修写程序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)。1 H0 X$ X# Z. b$ y
  I" s: I) o; K5 o! e
程序14-7 寻找第k 个元素" h: s% u! x" `6 D1 ~* H
8 J% l! }2 c# L7 y, p4 r$ w
template<CLASS T>% ^, y9 ~" P& V# v" }

- j5 W$ T+ n. U9 ?, M! XT Select(T a[], int n, int k)$ o( z( M% m1 Z/ V1 d6 E; j

9 P: @; w5 q% a{// 返回a [ 0 : n - 1 ]中第k小的元素
! o9 E! O2 I3 E
- y- U6 m/ A8 S3 F// 假定a[n] 是一个伪最大元素
+ e) H9 z$ m: w: H- N8 O) ]+ x" K5 f8 N
if (k &lt; 1 || k &gt; n) throw OutOfBounds();6 p8 x8 I5 z* s# _5 _/ @& [/ ?
( _+ ^/ G. O+ Y: U8 H2 e
return select(a, 0, n-1, k);; c) z! g! d" Y; p9 T
6 g, _' O3 b, e4 M5 |# H" G
}
! _5 p% [7 Z8 m# G. ], V/ r( H% Q4 K- t2 R& `- H- i/ {
template<CLASS T>% o" P: ?/ f: V: ^. M; U

! F) e+ r$ m1 H7 E' c  p& D9 wT select(T a[], int l, int r, int k)
! V" ^- n6 j) K* A8 f2 Q5 H; t$ P0 h! ]- o
{// 在a [ l : r ]中选择第k小的元素
0 m: r" C# e; y& ?# C  A! E. s
! ]. G9 v) @8 ~. [! _* y0 Zif (l &gt;= r) return a[l];( |" D) {& o" O% R7 h5 |& D1 X4 ^  _
2 Q& E8 _2 B7 E9 ?) m$ o
int i = l, // 从左至右的游标
/ K& D( C! A# F7 C
! Y0 e) k4 f+ i$ E+ k6 xj = r + 1; // 从右到左的游标3 ]& u; ]& W' b. V2 {# i$ N- Z  N

. v# |0 n' H' L5 I1 g  lT pivot = a[l];
. W6 H& Z) a3 \9 e
! L& b: L( Z1 ~  f5 P  L// 把左侧&gt;= pivot的元素与右侧&lt;= pivot 的元素进行交换; {; v' ~/ a& R- G5 q
& L& }) B$ a) @. E9 G8 ^' u7 ^
while (true) {
/ D' _' C, k0 g4 C  L& e$ A; V7 e! }
+ z8 m, A3 G0 m2 Odo {// 在左侧寻找&gt;= pivot 的元素
/ z) j' u8 `# i1 C( \
% F& f% Y+ A' a9 z9 j5 k7 R+ x) fi = i + 1;
3 B: T3 e; K. D( e; A& M9 u5 l; ~8 _* v6 [
} while (a &lt; pivot);' h1 G. }. g+ a/ k1 i) L; I

- D) ]- M4 F; d6 ydo {// 在右侧寻找&lt;= pivot 的元素
+ h( }/ E& Q6 }8 M* I
, Q; H5 r& w* t! U: uj = j - 1;
  g/ b+ @  w1 L2 K
+ @2 v: d: l' ?/ i; N& u) l} while (a[j] &gt; pivot);
% X2 e$ ^' v8 }/ B& S' S! ]2 Z. C' P8 Z* Q) y& t
if (i &gt;= j) break; // 未发现交换对象7 ?* E) o. C% P" w- |7 w6 s# z

0 u9 S) P, ~5 E; x" x, \1 V/ i/ ESwap(a, a[j]);- d" z8 {; b! s$ m6 b
& s. q) Q+ U' Y! C
}
1 `$ R9 Z4 f  A( z" K9 e9 j; p) S# N! J  ?0 o; p
if (j - l + 1 == k) return pivot;
7 I9 G  m8 `7 z
. b  g( Q) C) a5 J// 设置p i v o t
# x' ]% w& j8 f, O9 d
: M- O( w8 ]6 G' ~) M" k6 ia[l] = a[j];
' G4 e7 z# Z4 N. ]# T; W/ n; k9 }
3 U0 r$ Y% \) V3 d; la[j] = pivot;
! O$ T5 g5 b% ~, [( N' f
, ~4 s% K  h' y6 B// 对一个段进行递归调用
5 {1 V# Z2 t& v0 x# R2 ^
, ]. `% }# |/ E0 Nif (j - l + 1 &lt; k)% g1 X( s" C" Q) H* V, b

& d1 p" A+ v  i" D$ Qreturn select(a, j+1, r, k-j+l-1);
  F  F; z. }3 y3 K# P) |% W& K
else return select(a, l, j-1, k);
/ {- c, `$ `( w) |2 ]
( q: _% P; i5 y# M}( ]; p% t* }0 l% o
: q# g4 F4 o# J* P' Z. E
程序1 4 - 7在最坏情况下的复杂性是( n2 ),此时left 总是为空,而且第k个元素总是位于r i g h t.
/ ^/ a8 k9 H* r/ t3 K. q. M
# e6 Q5 x. s2 T. [如果假定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 个中间元素,递归使用选择算法,求得所需要的支点元素。/ T1 X9 K9 w! M+ H

2 `% I6 R% G9 ~( ~. |4 @; r/ F: F例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 ]。
/ V* Q% T8 n+ l5 j- i" Z7 Z9 E) t+ \9 Q. l; d; S( c$ o8 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 )个元素。( h6 u9 Q' o+ X* H" U. G, |% N

8 a" {3 `, j7 }: e# w* j0 M定理2-2 当按“中间的中间”规则选取支点元素时,以下结论为真:
8 H* g' ~5 ^7 x8 j5 J
; `  z4 p3 M6 ~- X1) 若r=9, 那么当n≥9 0时,有m a x { |l e f e|, |r i g h t| }≤7n / 8。# L& ~' ^8 P9 q
' i# {8 }% h5 ?! F
2) 若r= 5,且a 中所有元素都不同,那么当n≥2 4时,有max{| left |, | right | }≤3n/ 4。; A5 H" j5 w. k% }4 f/ w/ u7 Q

6 G& b6 [2 H! L8 T证明这个定理的证明留作练习2 3。1 V3 P5 j, F, l$ x$ x! f2 m

* U8 {7 n. c# x* ~+ t根据定理2 - 2和程序1 4 - 7可知,如果采用“中间的中间”规则并取r= 9,则用于寻找第k个元素的时间t (n)可按如下递归公式来计算:
6 K1 t3 F' e/ r, E: ^5 M4 ^0 S) j1 r
在上述递归公式中,假设当n<9 0时使用复杂性为nl o gn的求解算法,当n≥9 0时,采用“中间的中间”规则进行分而治之求解。利用归纳法可以证明,当n≥1时有t (n)≤7 2cn (练习2 4 )。- t/ D* N/ ?4 q+ W
7 X4 M3 K. |/ p# H+ T$ c# K' ]' {
当元素互不相同时,可以使用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-10-29 09:34 , Processed in 0.854612 second(s), 52 queries .

回顶部