QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3477|回复: 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% g' _& U# P+ c$ ]# \6 {' {6 t
! j/ X/ U  W) V/ d
选择问题的一个应用就是寻找中值元素,此时k = [n / 2 ]。中值是一个很有用的统计量,例如中间工资,中间年龄,中间重量。其他k值也是有用的。例如,通过寻找第n / 4 , n / 2和3 n / 4这三个元素,可将人口划分为4份。) H  X# F4 O7 u+ D% g

) G; g9 R6 E; T& B4 C选择问题可在O ( n l o g n )时间内解决,方法是首先对这n个元素进行排序(如使用堆排序式或归并排序),然后取出a [ k - 1 ]中的元素。若使用快速排序(如图1 4 - 11所示),可以获得更好的平均性能,尽管该算法有一个比较差的渐近复杂性O( n2 )。  y& q" l" g. Y( ]' c/ i
$ ^8 [8 n4 V* j- V% g+ w8 i
可以通过修写程序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)。. o7 J1 m/ e4 o) M2 _, B
% a3 B& |3 p. ~9 l
程序14-7 寻找第k 个元素
2 S( v; H* g1 O; q. @& Z, J. {5 ~! h, `
template<CLASS T>
0 C) z/ j& @: S% M% ~
1 E& J7 _/ Q" F6 p: E  @$ B$ JT Select(T a[], int n, int k)
8 q" j2 K$ U4 N; {5 x2 n. `; u1 J4 \5 B8 X1 S7 c6 j9 K
{// 返回a [ 0 : n - 1 ]中第k小的元素
; O" H3 [" f) f' B1 E) D* g& u; F
7 E! ]* ^$ T$ x// 假定a[n] 是一个伪最大元素
2 W# W/ u" B6 D$ d1 O0 v+ X' L5 |
+ Q) r2 O7 ]3 Q8 O2 j, Bif (k &lt; 1 || k &gt; n) throw OutOfBounds();
1 V4 l' _" Q. R" W- c
! f/ @0 D* J( T( G) [$ S! p( kreturn select(a, 0, n-1, k);& S7 r* X9 G/ m' `4 B, n" x
+ N; q: i$ _0 D9 ]" r
}; u. W3 c6 M6 z4 }2 E0 |
' ~( o( @7 w9 H" V. x; d; ~" ?/ B% c$ n
template<CLASS T>
6 A' x; m( N& q! _& c0 P3 N7 d. h6 J3 L8 I, D7 J- M
T select(T a[], int l, int r, int k)' S5 [, d- X. G" s2 s
$ U- I& {& L, [3 K" l
{// 在a [ l : r ]中选择第k小的元素& Z$ ~- |6 G3 \- e: |$ e

; q2 A; _. t2 Mif (l &gt;= r) return a[l];5 J5 t. @# j$ [$ s
: B3 Z4 p5 i/ }, x* C# H/ W  b
int i = l, // 从左至右的游标( [* a0 h% q- N: z% u0 U- l
, i! `, Q# G$ t! ]: r' D
j = r + 1; // 从右到左的游标( Q4 {6 I+ H- T0 X' Q+ O* F

! f; C0 ]4 J' h$ O  MT pivot = a[l];
+ t9 h! R1 t; B4 W2 p) v# H" v6 ^2 H; k2 N! d3 U5 @6 o
// 把左侧&gt;= pivot的元素与右侧&lt;= pivot 的元素进行交换
7 y# H" s4 |! _9 ~2 ^8 [5 _/ w4 ]8 v8 A. m' X/ [
while (true) {
+ G! t' \: y1 a4 Q
$ ]! z) b- G5 V5 T( h- y4 Ido {// 在左侧寻找&gt;= pivot 的元素) J3 v( H  b' _! m
8 I3 K+ s  C) J8 R
i = i + 1;0 n$ |2 d) B* D( L0 e( p: c( I: e
" ]$ Y- [4 \4 B/ R0 |
} while (a &lt; pivot);! t( p/ i; p! [

; ~3 W7 g8 |5 i1 ado {// 在右侧寻找&lt;= pivot 的元素; j9 I  u; W9 R" O7 ^
1 n2 ?. o7 X  T2 `5 J5 e
j = j - 1;
% n2 D, y2 P0 q  K- k6 n! M6 u
} while (a[j] &gt; pivot);. H' b: y7 A( U5 X( m
* Q& V, {( V% Z3 O. ~
if (i &gt;= j) break; // 未发现交换对象
1 K; H( M* v6 o0 u& g5 y+ B' O3 V6 r0 E) x/ S! ]5 F1 G
Swap(a, a[j]);* e* R# L: Q, V# \+ r, u4 v
% K) I1 o. r5 k* q
}
3 _. O8 P) ~* a: t% s  P2 P
9 a. z2 s$ [. {* Z% Yif (j - l + 1 == k) return pivot;
* E2 i7 O9 W( }( v( O, g* i& i3 H7 Z, T
// 设置p i v o t: w6 X) d' G2 y

, W, k5 M" x" Da[l] = a[j];/ o+ x, t9 Z9 Q7 Q4 j; p5 L1 [: j
8 |: M$ h( k7 M  y/ Z
a[j] = pivot;
- ^; I1 Q7 O+ b9 n" u
/ _# i3 n9 d4 B" _8 k8 Z# n// 对一个段进行递归调用, F$ S6 ?( @+ M. ]' L* C
: i% M: ]5 c; f' p2 X- ^
if (j - l + 1 &lt; k)) ^/ }7 l& S  p: f9 b( T% S2 `

' ~9 x& h4 S" M( r7 B- P, s) Vreturn select(a, j+1, r, k-j+l-1);0 w3 L% q4 T; z6 H0 H+ A. I: Z
$ F9 i6 @7 x, ?. {- K
else return select(a, l, j-1, k);; A* y6 j' K' b: x
1 `: X4 E7 i/ i/ a9 H' O
}
. V' E2 {9 i# {& @% |: Y+ ]
5 `7 I& L. `2 g9 {程序1 4 - 7在最坏情况下的复杂性是( n2 ),此时left 总是为空,而且第k个元素总是位于r i g h t.9 B/ k" W/ ]% B: \. ^

+ [2 T( D- T1 `! i& m如果假定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 个中间元素,递归使用选择算法,求得所需要的支点元素。
0 O7 P) K) x0 m$ [# p) J* x: w% s, S
例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 ]。
  }- p+ i. c. R+ `9 y  a6 q( O% W5 y( x
如果要寻找第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 )个元素。
( S* l$ g( a9 l
1 J8 m' U  H9 v' n) s+ e定理2-2 当按“中间的中间”规则选取支点元素时,以下结论为真:
" z& K$ e$ g* e1 E8 M9 j) k0 _2 |% k$ M! `* y
1) 若r=9, 那么当n≥9 0时,有m a x { |l e f e|, |r i g h t| }≤7n / 8。
6 f: o% r9 s1 [8 |- [( w$ @& Z3 L5 L. [* o+ |
2) 若r= 5,且a 中所有元素都不同,那么当n≥2 4时,有max{| left |, | right | }≤3n/ 4。) c; z) ?9 j4 l! \) O
) ]( w7 N) {" P; n6 C2 ?
证明这个定理的证明留作练习2 3。& Y& A) ]+ i( \
/ m  v8 [. c& \, v' F" L) y" D; v
根据定理2 - 2和程序1 4 - 7可知,如果采用“中间的中间”规则并取r= 9,则用于寻找第k个元素的时间t (n)可按如下递归公式来计算:
) B3 p& \* [0 B7 w
# q/ u; ~' ~$ K% l' N在上述递归公式中,假设当n<9 0时使用复杂性为nl o gn的求解算法,当n≥9 0时,采用“中间的中间”规则进行分而治之求解。利用归纳法可以证明,当n≥1时有t (n)≤7 2cn (练习2 4 )。
! V, ^9 ~8 }+ H' @* ]6 F- I- T5 C+ }" K3 {7 P9 @. q
当元素互不相同时,可以使用r= 5来得到线性时间性能。</P>
zan
转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
您需要登录后才可以回帖 登录 | 注册地址

fastpost qq
收缩
  • 电话咨询

  • 04714969085

关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

手机版|Archiver| |繁體中文 手机客户端  

蒙公网安备 15010502000194号

Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

GMT+8, 2026-10-31 14:00 , Processed in 0.391688 second(s), 51 queries .

回顶部