QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3465|回复: 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时被返回。
3 W* Q3 M; o$ S9 G) M& I  r% ?$ ~3 ~4 }9 V+ D7 ]3 M
选择问题的一个应用就是寻找中值元素,此时k = [n / 2 ]。中值是一个很有用的统计量,例如中间工资,中间年龄,中间重量。其他k值也是有用的。例如,通过寻找第n / 4 , n / 2和3 n / 4这三个元素,可将人口划分为4份。
4 N5 x; G. `: {) j: o% }3 p
8 H; Z* t. Y2 h+ @/ P选择问题可在O ( n l o g n )时间内解决,方法是首先对这n个元素进行排序(如使用堆排序式或归并排序),然后取出a [ k - 1 ]中的元素。若使用快速排序(如图1 4 - 11所示),可以获得更好的平均性能,尽管该算法有一个比较差的渐近复杂性O( n2 )。
! D5 o2 M; r. T
! i) u" T# V+ T9 P8 F可以通过修写程序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)。) G- N& V9 w6 K; x# v( y. w
. B/ @2 G3 E5 T: ~) j1 `3 r
程序14-7 寻找第k 个元素# g5 N9 l  Z+ H- g7 B+ x$ s
% c8 |) j# T! E4 ?& S  ?/ ]
template<CLASS T>. ^+ t9 E  X# U+ N( T
- e. K9 P) W2 D+ K) Q$ I) _. [
T Select(T a[], int n, int k)
$ y7 ^5 b( ~) k, g
/ r4 d& |' a8 A: m{// 返回a [ 0 : n - 1 ]中第k小的元素
7 R1 L( u" A$ I
  E4 q- m9 O; `- Y3 y// 假定a[n] 是一个伪最大元素
' Q: [' _# z1 _- o0 a
. |6 L: v; j$ G  e/ m$ e+ K) y0 ]if (k &lt; 1 || k &gt; n) throw OutOfBounds();
1 A/ x7 g1 o2 w3 S
$ ?/ A: P- ]% y1 hreturn select(a, 0, n-1, k);
9 R4 l0 f+ I' }# H
/ w. z' Y$ X6 W% q/ u3 e* H}
* I" H2 d! `: y" g4 D) x
& D. d' N( b. T5 E! n& itemplate<CLASS T>
" i) c  Q) b- [
( Y7 Z: s) S1 z. RT select(T a[], int l, int r, int k); i6 I  n1 H! J( g- l
- ]! A; m- o; o; {  [4 X/ I. ~
{// 在a [ l : r ]中选择第k小的元素4 W  u3 T2 D/ a
" `' e3 y' [' a! c4 v
if (l &gt;= r) return a[l];3 B% \( q% }3 a8 ]9 d

/ ?- q! u& A' R0 [  D" mint i = l, // 从左至右的游标, J0 V. g( K4 o8 T- Q

5 y7 [5 ]( p3 Aj = r + 1; // 从右到左的游标) c9 d* e2 \) m2 U5 v; i; Y
2 Z0 c! D- j# M0 }) P7 I
T pivot = a[l];
: ~9 U/ H0 b1 _+ v- J
! ]: |& }% H3 s# o5 l// 把左侧&gt;= pivot的元素与右侧&lt;= pivot 的元素进行交换: C% G9 m2 e0 c

7 Q* P2 e4 n8 e4 f0 Cwhile (true) {
. C4 W$ z3 W3 n4 t
" E+ \* j& f4 q1 z2 S' Udo {// 在左侧寻找&gt;= pivot 的元素" x# B$ H  T6 Y0 J  }, L9 T: }# O

2 I! m( e! ^2 Ni = i + 1;4 q" M0 @5 R. \% B
, f/ M& q% T1 b6 g+ x
} while (a &lt; pivot);: H- R% |# P5 {
9 H! L7 Y6 D2 I' I" N# v2 H
do {// 在右侧寻找&lt;= pivot 的元素# e1 {* E+ c5 P+ K$ p

9 o+ |! F, d6 {+ o# p3 x7 Ij = j - 1;; W( ?" B! m3 A% Z

3 t) M6 R* i4 r1 x" g} while (a[j] &gt; pivot);
! a) |: T" q( p* Z5 B( Z7 ^
# C$ D# ?. N4 j# R, h3 mif (i &gt;= j) break; // 未发现交换对象
& @" T% O; E" M( J* _  I, p& {1 O$ Y- j# J" e
Swap(a, a[j]);1 E% Y) ]) W+ P" |, E
+ _1 F5 c8 P( }9 c
}% Z# \1 H) d) I4 @2 J. u: N; W, K
& `+ W$ y) A/ O
if (j - l + 1 == k) return pivot;
$ Z! P% P& ~% s; [7 _4 K! O7 {: w, E$ E+ k+ v5 L
// 设置p i v o t4 M. l9 n- ^: _; ]- z, w9 T

  g) V# ?% q& ?! r' X* Xa[l] = a[j];
3 S3 ^9 A; G9 N. M7 y+ c
7 ]' g) B- l# u8 ha[j] = pivot;
  G4 \6 J( s0 o+ P6 x/ k4 O3 Y8 W6 R4 A
  f- M7 {! n: |// 对一个段进行递归调用* a) K: N4 s3 n

7 e! J: U6 s9 g( u* j$ B1 d" tif (j - l + 1 &lt; k)
+ Y( j$ C/ ~1 n: r7 f3 B  C6 h( B/ A6 e
return select(a, j+1, r, k-j+l-1);0 x) ~  W; s/ l7 t3 `+ E

, I+ u& v& {& r0 H- jelse return select(a, l, j-1, k);/ K3 w& W5 b+ A9 k, z3 h% Y
& f% u6 U" E- ]5 u, ?$ @1 @7 n# B
}4 O0 {$ M* P7 o( a+ n
; F9 p) n5 ^" x" z2 p
程序1 4 - 7在最坏情况下的复杂性是( n2 ),此时left 总是为空,而且第k个元素总是位于r i g h t.' |; @2 ^) Y8 ?. y
2 f* [. S' t" y6 [' 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 个中间元素,递归使用选择算法,求得所需要的支点元素。
, f  {; ]- j& z( V* u. l
6 a7 Z, I) g& n8 r- ?- W9 x3 [例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 ]。$ ?; R) C0 f/ P# v! A

' N5 I  {; ^& [: J% K如果要寻找第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 )个元素。, N2 J8 v6 ?2 h+ w; I

4 L8 Y1 s+ X% `- P定理2-2 当按“中间的中间”规则选取支点元素时,以下结论为真:
) ?$ m3 u$ A- }0 A
7 o. U, D% {$ `; x6 {0 Z1 \# |, K# h1) 若r=9, 那么当n≥9 0时,有m a x { |l e f e|, |r i g h t| }≤7n / 8。  D$ e) [9 _4 _4 O( G/ _, ~

/ }0 Q! a! f7 e9 a7 V! r3 ?1 i6 P% q2) 若r= 5,且a 中所有元素都不同,那么当n≥2 4时,有max{| left |, | right | }≤3n/ 4。
$ Q6 B' i" t6 R9 l2 j, e# J- }6 S  b* d
证明这个定理的证明留作练习2 3。
8 m7 k. Y# p: o3 Y
# I  g" H, k0 S1 E0 }根据定理2 - 2和程序1 4 - 7可知,如果采用“中间的中间”规则并取r= 9,则用于寻找第k个元素的时间t (n)可按如下递归公式来计算:
- X) h/ ?' |# |! A2 x. n
7 J( C7 k9 Q5 D0 h5 \7 r* c在上述递归公式中,假设当n<9 0时使用复杂性为nl o gn的求解算法,当n≥9 0时,采用“中间的中间”规则进行分而治之求解。利用归纳法可以证明,当n≥1时有t (n)≤7 2cn (练习2 4 )。
( f6 g$ r* S4 C6 x. c0 g
" h- T2 K" Y1 T2 [0 e当元素互不相同时,可以使用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-26 04:11 , Processed in 0.411305 second(s), 52 queries .

回顶部