QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3369|回复: 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时被返回。
% }0 \3 J: A; D* E, ?( k& }. a  V  P. S; X4 w( {$ @6 A" `
选择问题的一个应用就是寻找中值元素,此时k = [n / 2 ]。中值是一个很有用的统计量,例如中间工资,中间年龄,中间重量。其他k值也是有用的。例如,通过寻找第n / 4 , n / 2和3 n / 4这三个元素,可将人口划分为4份。
, U. R( R. a/ d+ z
/ J* I& D9 q8 Z! l( o$ \5 c选择问题可在O ( n l o g n )时间内解决,方法是首先对这n个元素进行排序(如使用堆排序式或归并排序),然后取出a [ k - 1 ]中的元素。若使用快速排序(如图1 4 - 11所示),可以获得更好的平均性能,尽管该算法有一个比较差的渐近复杂性O( n2 )。" b/ w4 b- ~. f' ?  `' d+ v
9 q8 {% y) H0 _( {
可以通过修写程序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)。
6 c, k( b+ X- {5 ^% p
0 U& y2 d4 `9 A程序14-7 寻找第k 个元素/ T% j# j( D7 M/ P) d" @

1 G* M* }/ |% _! a6 T# Ctemplate<CLASS T>! ~: L% u9 C: v; X: A8 t' J
4 g4 q+ n1 A. [! M
T Select(T a[], int n, int k)" {% c. x2 x, g; W" c: x3 a+ z
0 A  `" h9 A7 l
{// 返回a [ 0 : n - 1 ]中第k小的元素
( D, G/ T1 j- o9 b
* B: v1 p" ]0 S$ s5 r; d3 I// 假定a[n] 是一个伪最大元素
) `: N7 w* y) Q: C+ y% y3 @% y5 l/ l0 S/ x
if (k &lt; 1 || k &gt; n) throw OutOfBounds();: ]; \8 n5 v: ^

2 g9 a9 K# ~% `return select(a, 0, n-1, k);- u, q9 [/ o* k& K  ?  v. G
/ {# P& i4 ]* ~* r4 Z
}5 V% Q4 L- m7 h# o  r( y

4 t7 p6 L. K) m: {5 n( {4 M( vtemplate<CLASS T>* v7 ]# D% h2 v
# c% J4 A6 F0 G1 t
T select(T a[], int l, int r, int k)0 @6 }' a" f3 f2 S

5 T) _1 r9 b* l) V& m% u% v{// 在a [ l : r ]中选择第k小的元素
0 M1 h5 R# U  j" Y4 {& ^0 A/ r( o
if (l &gt;= r) return a[l];
: u7 j. K8 q" p2 l* {
% \0 t0 q1 @# w. vint i = l, // 从左至右的游标
) [3 O0 t6 M4 [3 S' Q' R6 w6 o9 u' w
j = r + 1; // 从右到左的游标" m% r/ e& S' p* h* @4 g
* d' B, e: b" H1 [: m$ v- ?6 F. F
T pivot = a[l];
! \" t0 z3 R! t3 ]* d1 ^. V) Y5 T% f* \- _) s! O$ a
// 把左侧&gt;= pivot的元素与右侧&lt;= pivot 的元素进行交换
. x1 y2 a' J" v+ H* R& H$ O6 C* j$ Y7 ]# S. d' L! e
while (true) {) C  ?! j: Y4 \1 i
# J0 b2 P: A# b- H
do {// 在左侧寻找&gt;= pivot 的元素
- t( U" C# G. ~) i$ o, N1 z% W' _2 O1 l5 t+ l/ U
i = i + 1;+ m4 H+ C, H8 C+ x0 q7 Z

0 I  q3 k! B( r$ m( ^+ o# F8 J} while (a &lt; pivot);
$ N/ F! g' R4 B  b# h1 x( y
! A, L2 m" ^) I0 b1 B0 n$ A) ^3 ?$ N/ u# zdo {// 在右侧寻找&lt;= pivot 的元素$ [/ M4 F' m6 r

& N) S/ z/ a. f& N5 @3 Wj = j - 1;
7 B; N$ O' A; W+ M; I/ b# A, |# }- @7 e; }+ ~
} while (a[j] &gt; pivot);
( u9 ~8 ^, @/ P# _0 [
" @" F6 ]8 w5 E+ s2 P5 T! k9 L0 cif (i &gt;= j) break; // 未发现交换对象
1 G$ I+ s* m7 L0 |4 W, I$ Z2 u" ?  F* J5 X8 v
Swap(a, a[j]);
/ d9 A. m) p6 g0 B6 u8 v- A
" H3 m) Z( i7 M8 f! g}
7 `# k1 M7 j9 k9 [6 a, q% e8 H
2 z0 W/ n" x: u3 f3 gif (j - l + 1 == k) return pivot;$ Q! c. l$ f' d

2 F1 C' |, t/ N. R" y9 c2 f// 设置p i v o t) q9 R4 W6 e9 u# i5 ~5 U% e, E
1 j9 n. n. S  e( L- z) `2 l  Y
a[l] = a[j];* M5 a) ?9 o" r

% X* q; L  a3 c$ O2 d2 ya[j] = pivot;0 B, n1 c% Q- l  o
+ W  b+ e  Z3 K
// 对一个段进行递归调用
  H& u* m5 M5 |# j. O! W! h$ O# s) c% V4 S
if (j - l + 1 &lt; k)' |2 x) y4 [% E3 N. ?: w
* p( m" B4 h1 L3 w7 x$ b0 s
return select(a, j+1, r, k-j+l-1);$ i- f% o, B/ C. E' {

2 ^3 O% y! J2 {- Belse return select(a, l, j-1, k);7 w( A# E9 y0 R% w9 M/ \( g3 N

* `( L) O+ E& m; R}
' w4 |& ~  y" O; @# H! O% h
  j. n0 w. n: i4 @  q程序1 4 - 7在最坏情况下的复杂性是( n2 ),此时left 总是为空,而且第k个元素总是位于r i g h t.; @# S2 y' K9 S# k' O$ z
+ `1 `( }8 u$ S+ v5 d+ f
如果假定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 个中间元素,递归使用选择算法,求得所需要的支点元素。
, D% m  t: t; s8 {3 r
; w7 K- }" t4 c- t1 r5 X例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 ]。
% |  W# U2 f! Z7 J' i# j; S* X
" ^! |* `, e; ?6 J如果要寻找第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 )个元素。5 P  T# m8 ~: a4 P1 F# Q

0 Z. Q% F% k& W- t定理2-2 当按“中间的中间”规则选取支点元素时,以下结论为真:6 h, o$ b3 k% V' k. w% h

2 ^5 w" ~! `  _; J- s" A6 S1) 若r=9, 那么当n≥9 0时,有m a x { |l e f e|, |r i g h t| }≤7n / 8。2 e5 s8 P. A6 V2 t( b, |

/ L7 l# Z! z' J- u! j2) 若r= 5,且a 中所有元素都不同,那么当n≥2 4时,有max{| left |, | right | }≤3n/ 4。' T, b( F4 H+ B! {# y) C
7 |; z" R; o: i5 l
证明这个定理的证明留作练习2 3。
1 \0 B- ]4 `: j* i. v! s$ H1 X( `% f
. j6 s; N( \' H& }根据定理2 - 2和程序1 4 - 7可知,如果采用“中间的中间”规则并取r= 9,则用于寻找第k个元素的时间t (n)可按如下递归公式来计算:
& l5 {, \) ], D9 ^! K5 J7 L9 K( D9 a4 n8 S
在上述递归公式中,假设当n<9 0时使用复杂性为nl o gn的求解算法,当n≥9 0时,采用“中间的中间”规则进行分而治之求解。利用归纳法可以证明,当n≥1时有t (n)≤7 2cn (练习2 4 )。7 W  i2 p$ @9 R5 G) _4 H

! r% t: v5 G% I( I当元素互不相同时,可以使用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 06:14 , Processed in 0.317685 second(s), 52 queries .

回顶部