QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3373|回复: 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时被返回。$ T( Z% I; U" k. D. Q' H/ i

* J5 Q: C' u8 O选择问题的一个应用就是寻找中值元素,此时k = [n / 2 ]。中值是一个很有用的统计量,例如中间工资,中间年龄,中间重量。其他k值也是有用的。例如,通过寻找第n / 4 , n / 2和3 n / 4这三个元素,可将人口划分为4份。/ q$ F6 q" N! P* ^
" S' q! `* ?" j3 k/ i3 O
选择问题可在O ( n l o g n )时间内解决,方法是首先对这n个元素进行排序(如使用堆排序式或归并排序),然后取出a [ k - 1 ]中的元素。若使用快速排序(如图1 4 - 11所示),可以获得更好的平均性能,尽管该算法有一个比较差的渐近复杂性O( n2 )。
! b# i/ J* }4 d+ M2 ^; |
# c# N' I2 b6 [" d! b9 ]可以通过修写程序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)。
& a) d! y* g& t( [- f; b/ B
4 a* m4 e: C- T- r# d! a& V程序14-7 寻找第k 个元素
% v: y- g+ {) Q! f0 V% ~  p6 ~* R- L# y8 q- c4 c
template<CLASS T>/ X$ r1 g" W- \$ B4 Z5 N0 N
9 `; {8 ]$ w3 F0 E; C& a
T Select(T a[], int n, int k)
1 I+ e. t: f& a( Z0 d1 i. l( J- R* u+ L
{// 返回a [ 0 : n - 1 ]中第k小的元素
1 O8 w; A7 y+ P; m! X6 c8 l. A* a/ \0 u% t5 P2 o0 V
// 假定a[n] 是一个伪最大元素
6 y" E& f" C  E3 `: K* Q! e+ u. ^; o
if (k &lt; 1 || k &gt; n) throw OutOfBounds();
8 ]! }0 q- H% x0 G; _+ L! h4 Y4 ~2 t/ ]( ^, G7 z
return select(a, 0, n-1, k);
$ Y7 O- A0 S( T, \3 c
" y  B! o1 k/ a}
% m6 }5 s' L% t. e/ i& O: n. o# ^# C  M8 V
template<CLASS T>7 k# A+ T4 k$ |0 q9 x

% Y6 d+ n: x0 BT select(T a[], int l, int r, int k)7 [# J* A9 Y6 v+ |3 C( f

1 S+ a! ^/ [, c/ q{// 在a [ l : r ]中选择第k小的元素) a* v4 g2 h8 @

' f" r6 s: d# f" Jif (l &gt;= r) return a[l];# g+ g5 P, o$ |+ j* w. N  Y( `

, _: M6 p2 v3 v2 }0 }( ?. Aint i = l, // 从左至右的游标, m2 V* y) @* [) O

; d+ d  S) w: z3 |, pj = r + 1; // 从右到左的游标
6 f7 `# y$ N4 s* X' p9 L
" y+ Q  |: u8 ^) l) o3 zT pivot = a[l];
+ w' P$ a; x4 \$ z% O3 V6 W: b  s" Q4 d
// 把左侧&gt;= pivot的元素与右侧&lt;= pivot 的元素进行交换
1 B7 ?' }' H, v6 O* C$ E+ v& J
8 B/ L$ }  y$ D/ u7 g% B& Ywhile (true) {) K+ k2 d) U8 U, l
) x' ?( A1 J$ W3 X5 A6 s
do {// 在左侧寻找&gt;= pivot 的元素
, V# T3 O! O$ c0 C. }4 u3 I  b, [7 ?( D3 r
i = i + 1;: V* D8 _# h2 N5 l, _( b
7 E* c# R8 }% x, M) B
} while (a &lt; pivot);
/ o) H  H! o7 ^( R: q( r
9 l; y0 W2 c7 w. Z3 Kdo {// 在右侧寻找&lt;= pivot 的元素3 n. x. E% d0 ~
; X) m) N6 l2 y0 N. x
j = j - 1;, u/ S8 R! F2 Y) k3 t3 f  O
, w6 f' R! ^/ \
} while (a[j] &gt; pivot);, `8 \( V( e" C
+ j. h  K- J/ Q  N# m; d
if (i &gt;= j) break; // 未发现交换对象
! P% G) @" G7 N1 ?+ y/ ?4 ^
  J3 u% ~* S: g  g# l* I+ s; PSwap(a, a[j]);# W( u# ^3 G* l

9 S; w9 r# V4 K8 b* k! g' O}
9 Q7 Q9 n  {) b# G5 J- ]6 `4 b
% C+ G7 F" Z# x7 V4 {1 yif (j - l + 1 == k) return pivot;, p' {+ m9 v8 E  Q/ `/ D2 H

& X8 Z$ L# }. [+ C0 ]3 ^! T// 设置p i v o t
8 K. [' @6 w8 P( J) c$ b8 `) m9 ]4 S+ M' p
a[l] = a[j];
/ O2 w0 ^, `3 K& r  t, Z( M6 q( G: U$ m: J- S. h
a[j] = pivot;
. q4 ~, L1 h( G* L. n; T7 i
$ q' l+ c5 J) T+ O3 _// 对一个段进行递归调用3 G0 H3 W' j& H1 {. M

) K/ \; w- V. Uif (j - l + 1 &lt; k)
" V+ `" Q! a- F: q3 @
9 y5 d2 h2 v+ P6 J9 Nreturn select(a, j+1, r, k-j+l-1);) o4 y% g. G  M) Z1 O
  z4 t% y" u0 j6 x
else return select(a, l, j-1, k);
- m* N# v0 d# X4 M; V7 z# n- i
4 ^0 v! `0 z% W1 X% o}% i8 B8 h2 |, w( [7 D0 @4 O

8 R2 }$ F6 C" [程序1 4 - 7在最坏情况下的复杂性是( n2 ),此时left 总是为空,而且第k个元素总是位于r i g h t.0 S7 Q6 C4 n  T: t+ |

7 X! x0 [) @5 z1 R如果假定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 个中间元素,递归使用选择算法,求得所需要的支点元素。+ I2 J8 W3 G5 U( M* n. t2 {6 t

8 t5 c) V- y$ S" p+ `( e$ X* z' M例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 ]。1 E5 f0 H0 G2 t4 J9 q+ D' ~5 m3 Q

2 z& y, i1 s* j: R6 ?" E如果要寻找第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 )个元素。
) F. n# q/ }( ~' g. G! {4 B3 a# a6 |9 j: P6 }
定理2-2 当按“中间的中间”规则选取支点元素时,以下结论为真:
) i5 O& X* J9 x3 {6 I, N9 p
0 M/ y; p" \% t3 {! U1) 若r=9, 那么当n≥9 0时,有m a x { |l e f e|, |r i g h t| }≤7n / 8。$ _2 Q* S8 L0 n! X

, r  r  z  w  r& m2) 若r= 5,且a 中所有元素都不同,那么当n≥2 4时,有max{| left |, | right | }≤3n/ 4。
- A& r+ d7 w% ^. B2 |% x7 x3 M* q8 x9 {! j" O# c
证明这个定理的证明留作练习2 3。
& w( T. X( V* a. C4 K! ?# r8 A0 E5 C: d
根据定理2 - 2和程序1 4 - 7可知,如果采用“中间的中间”规则并取r= 9,则用于寻找第k个元素的时间t (n)可按如下递归公式来计算:
% S( B. t2 s# n9 L9 N" T8 F% x1 {# M+ k; O% ^3 s
在上述递归公式中,假设当n<9 0时使用复杂性为nl o gn的求解算法,当n≥9 0时,采用“中间的中间”规则进行分而治之求解。利用归纳法可以证明,当n≥1时有t (n)≤7 2cn (练习2 4 )。& p: r, B9 M/ h  v/ r
" K- _# L1 U+ Z( M, M% p
当元素互不相同时,可以使用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-4-20 13:42 , Processed in 0.914054 second(s), 52 queries .

回顶部