QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3399|回复: 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时被返回。; P. x) p, h4 g! e+ \, p1 U

% B. F9 T. _( V( v+ L% N选择问题的一个应用就是寻找中值元素,此时k = [n / 2 ]。中值是一个很有用的统计量,例如中间工资,中间年龄,中间重量。其他k值也是有用的。例如,通过寻找第n / 4 , n / 2和3 n / 4这三个元素,可将人口划分为4份。  h- v; C' B+ {! F6 O/ H

8 R* j, \8 w* F5 S7 u& P选择问题可在O ( n l o g n )时间内解决,方法是首先对这n个元素进行排序(如使用堆排序式或归并排序),然后取出a [ k - 1 ]中的元素。若使用快速排序(如图1 4 - 11所示),可以获得更好的平均性能,尽管该算法有一个比较差的渐近复杂性O( n2 )。( f  X& f) C6 W

4 _- q. o: F0 g" {* ?可以通过修写程序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)。! K* H1 T: a. e4 x
  l) R1 r3 q2 ^8 Z  k
程序14-7 寻找第k 个元素+ u) F  J) k. o/ Y. |  p- v

4 _; _1 x/ D( {, ytemplate<CLASS T>8 Z6 ~* o* c+ u

2 C9 x' p1 Y% L0 Y8 ]T Select(T a[], int n, int k)
( C9 a4 k+ B- j! T5 c
$ D5 H3 `- S7 ~7 i{// 返回a [ 0 : n - 1 ]中第k小的元素
9 L) i! b7 E8 L; m: p6 \4 H# s7 r0 g; s
// 假定a[n] 是一个伪最大元素
9 W' h$ e" o- ?$ C2 G
  _4 B/ Z0 J* \4 D. \if (k &lt; 1 || k &gt; n) throw OutOfBounds();
( p% j1 }/ t6 S: P5 }
4 g( x$ R& M6 Y5 P; q. Kreturn select(a, 0, n-1, k);" I$ R2 e! h. o- a: Z
+ Y$ ?# }' Z( M. ]2 G* A; p+ i
}
7 @* W+ @9 B& I6 M4 L3 }: s) S( ~5 b! }
template<CLASS T>
$ B2 {' ^; [6 X, B: \7 Q. I; k* t
$ j0 D; Z, ^6 m/ D! K3 r* Z- OT select(T a[], int l, int r, int k)0 f) n. ]! m5 T0 Y5 J1 w
" w7 c2 @% I/ @2 f0 r
{// 在a [ l : r ]中选择第k小的元素/ l( @9 }( [6 ^; k8 c9 p/ y
$ P0 z/ c; s2 T  P
if (l &gt;= r) return a[l];7 M; ^# d1 X3 D' U+ y

4 v5 a1 O# {6 \$ q4 Sint i = l, // 从左至右的游标
, Z8 y" ~5 o' z" X: K# B; n4 j
% i. @% T; E5 b9 t: kj = r + 1; // 从右到左的游标
& A, J' _9 k2 w5 X) Z1 O9 z
$ R/ k* b$ a* i/ M2 wT pivot = a[l];
0 Q+ ?# P2 m) v- u) p2 o5 ]$ X' i9 h! o) [7 t- T; |. V. a3 D
// 把左侧&gt;= pivot的元素与右侧&lt;= pivot 的元素进行交换" E+ R, ^% f) P; q2 y7 N
) |9 _7 T( }8 `, j5 P! s: o8 {
while (true) {
; ^4 T- ^' n0 u, N% `: m) O6 A. v9 G) i/ @; V
do {// 在左侧寻找&gt;= pivot 的元素1 h4 V- r9 }6 q: I1 W# o+ J
. S4 _) U6 I: l* d: ]/ A6 N) n
i = i + 1;
, F$ _  D: R4 ~: n. w' A5 R& w) ~; s* k1 N& z( w/ c
} while (a &lt; pivot);
% D, N6 a) l8 k
+ E) ^% N* s- y1 ^: O. h6 C3 Fdo {// 在右侧寻找&lt;= pivot 的元素
( j/ `$ i, f4 K+ p" ?
/ H& g, E1 d/ Q( F" q9 J' \5 \j = j - 1;5 v8 P7 V6 ~  m% \" I, u9 D5 W5 r- b

* |7 m0 B- Y' H( A% B, g2 K6 k- B; \} while (a[j] &gt; pivot);5 p* Y( u; \1 }4 r: J

' B+ P0 j5 M( E6 l; I3 R) Fif (i &gt;= j) break; // 未发现交换对象# R# D8 w# I5 n, ]% o- g+ Q
8 ^) u# ?. _+ R/ x# a- J$ q
Swap(a, a[j]);, V" w  H/ W' m7 ?* s" _  x! Y" ]
& c, J; b7 A* U( \
}5 f6 Q% d9 j& h& I* y
% q* j. D- R- {& A4 j* K
if (j - l + 1 == k) return pivot;, i+ s1 G6 G: N5 h7 w

4 z6 b( `. y5 _4 s" [// 设置p i v o t* l% _1 @2 t6 Q0 `% `" l

* y9 s+ ?( k" q% Na[l] = a[j];
2 L8 G1 u) Z: d9 R# g/ }
0 ~+ a' p: a, ^) t$ Pa[j] = pivot;6 l3 j0 I2 S# {
; D8 ~& @/ H8 W$ t
// 对一个段进行递归调用
& S( [6 ~- F/ q/ [. S6 l- e2 U& |4 L. }# E/ Z% {. r0 g9 I
if (j - l + 1 &lt; k)
' ~! {+ Z+ v4 S8 b2 ^/ z% ?/ ?2 Y- |( ^
return select(a, j+1, r, k-j+l-1);
5 I" P$ B& a: x/ }: Q& b
7 d9 b4 k+ E# `: @* ^- Gelse return select(a, l, j-1, k);
5 N# i" q$ T" S! \# Q; _7 L$ P5 t  D2 P; C- l7 u
}$ C! `* `$ i! I! }* s

8 z) u* q5 R: d+ x7 q3 Q程序1 4 - 7在最坏情况下的复杂性是( n2 ),此时left 总是为空,而且第k个元素总是位于r i g h t.
8 n7 X& D* n7 Y* V2 A9 N8 U* q/ h) j+ c# Q. u, o
如果假定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 个中间元素,递归使用选择算法,求得所需要的支点元素。6 n; k, R: W. q; ]; A
: L, L% n6 [/ [: T4 X6 K
例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 ]。
7 s; f# n, M9 ?# G9 i: g$ K6 g1 y4 q. M$ J' K  `+ b
如果要寻找第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 )个元素。6 Y! S, C/ W; @  n. H
$ z5 o" F! s9 p7 R& t! \
定理2-2 当按“中间的中间”规则选取支点元素时,以下结论为真:
; {7 A3 X5 m* C5 W/ f
  j6 g4 ~8 Z+ H4 f1) 若r=9, 那么当n≥9 0时,有m a x { |l e f e|, |r i g h t| }≤7n / 8。0 a. g, P5 V) Z% i3 m- {3 A- d
+ D2 I3 G# S5 N) K) j
2) 若r= 5,且a 中所有元素都不同,那么当n≥2 4时,有max{| left |, | right | }≤3n/ 4。# q. q/ M" f, H" h' \( f
( A" z5 Z5 I9 e
证明这个定理的证明留作练习2 3。
+ j9 U( L  L1 ?& a, @) `1 b8 _( G* _% m9 v5 S& w4 h6 \) E$ V$ D
根据定理2 - 2和程序1 4 - 7可知,如果采用“中间的中间”规则并取r= 9,则用于寻找第k个元素的时间t (n)可按如下递归公式来计算:9 x  w1 _1 ?# r" J/ s

( k) b* t1 c" f( N$ A- d在上述递归公式中,假设当n<9 0时使用复杂性为nl o gn的求解算法,当n≥9 0时,采用“中间的中间”规则进行分而治之求解。利用归纳法可以证明,当n≥1时有t (n)≤7 2cn (练习2 4 )。5 A4 ~8 e" i7 b; U9 D, w

+ ~% y6 z6 |9 e- M2 |当元素互不相同时,可以使用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 10:43 , Processed in 0.403981 second(s), 52 queries .

回顶部