M8 E/ P* B9 {" g5 N2 l$ ` }p u b l i c :. y, G+ {( W* a6 E& d, F! }4 b0 |
) q1 y- b% X6 Y- V! X/ F
int operator<=(Point2 a) const + b: t/ P( B7 J" S, p2 o+ C 4 A9 f% X; d# n r2 q, Z{return (y <= a.y);}0 I2 Z- f7 Q; n) J# w
9 Y- v3 q' U3 O' Z$ lp r i v a t e : * [7 ] L3 w v( I t$ j$ P$ f A r5 ], T4 Y( aint p; // 数组X中相同点的索引 * k- j, J0 W1 j4 j9 b+ y' R P" f+ i t `
float x, y; // 点坐标" m% H7 K6 K0 l2 D1 g
* `4 G3 d6 n- j7 O; z: N- w6 m
} ;" e3 v Y. O& ^) a% {
: f5 U; f: h5 b( {4 @+ b所输入的n 个点可以用数组X来表示。假设X中的点已按照x 坐标排序,在分割过程中如果当前考察的点是X [l :r],那么首先计算m= (l+r) / 2,X[ l:m]中的点属于A,剩下的点属于B。计算出A和B中的最近点对之后,还需要计算RA 和RB,然后确定是否存在更近的点对,其中一点属于RA,另一点属于RB。如果点已按y 坐标排序,那么可以用一种很简单的方式来测试图1 4 - 1 6。按y 坐标排序的点保存在另一个使用类P o i n t 2 (见程序14-8) 的数组中。注意到在P o i n t 2类中,为了便于y 坐标排序,已重载了操作符<=。成员p 用于指向X中的对应点。& Y n/ \6 R5 u$ t
. ^7 S3 H) P8 F$ t P# F ]- V
确定了必要的数据结构之后,再来看看所要产生的代码。首先定义一个模板函数d i s t (见程序1 4 - 9 )来计算点a, b 之间的距离。T可能是P o i n t 1或P o i n t 2,因此d i s t必须是P o i n t 1和P o i n t 2类的友元。" a3 A4 o: L* W
" X* @6 @( C, L8 {0 ^
程序14-9 计算两点距离 m) ~. l3 i3 d 3 l t @, e* g' M8 {& Q L, ^' Gtemplate<CLASS T> 4 v% k+ [/ t" t6 c) f7 ~0 s" u * h4 d, Y" E9 Y/ ?, E5 [. winline float dist(const T& u, const T& v)% y4 ?) |2 N& x* q5 J
: r0 I% p2 r# w% I2 e+ h3 l{ / /计算点u 和v之间的距离 ) ?$ i; h; E2 W+ S - k! n6 Y, D2 p) x3 Rfloat dx = u.x-v. x ; , l- C4 K$ K/ V3 v ( f3 J& x* A( g* pfloat dy = u.y-v. y ;4 n2 D# @! q& F
* G% d( W4 h. A3 p lreturn sqrt(dx * dx + dy * dy);$ @8 S! L; l& b4 R! G, |) P
* z P+ t1 `! d} : a) m4 Z/ t* V m9 z& g 7 D. e" `- T* w4 L d7 z如果点的数目少于两个,则函数c l o s e s t (见程序1 4 - 1 0 )返回f a l s e,如果成功时函数返回t r u e。当函数成功时,在参数a 和b 中返回距离最近的两个点,在参数d 中返回距离。代码首先验证至少存在两点,然后使用M e rg e S o r t函数(见程序14-3) 按x 坐标对X中的点排序。接下来把这些点复制到数组Y中并按y 坐标进行排序。排序完成时,对任一个i,有Y [i ] . y≤Y [i+ 1 ] . y,并且Y [i ] .p给出了点i 在X中的位置。上述准备工作做完以后,调用函数close (见程序1 4 - 11 ),该函数实际求解最近点对。 Z! W8 H3 S( |- {' R' e+ ~+ K5 P
: {) F' {, |2 Q程序14-10 预处理及调用c l o s e - ^5 z0 F1 {. w( F4 e - G& D) [2 `& Q9 pbool closest(Point1 X[], int n, Point1& a, Point1& b, float& d)" [! ~* l! r! V
1 A. F8 a5 p/ m! Y w. W( `
{// 在n >= 2 个点中寻找最近点对1 M- J! \5 H: y o3 V" [
! L5 z( e% b. Z7 P" v: ^// 如果少于2个点,则返回f a l s e 7 A& J7 Z& R. ~" R6 A$ c, t0 b5 j, Q, w5 R6 s
// 否则,在a 和b中返回距离最近的两个点 & ]% \% v* M' ^$ Y6 ~% ] & v/ s1 y, h/ |# ~6 Hif (n < 2) return false; # S+ x' s% N" k) f5 c # O1 f; D# ?9 y, _// 按x坐标排序 # T% o; _! P% F ! c" \6 F& `8 rM e r g e S o r t ( X , n ) ; . ` v1 P: F* E2 \6 w! K) V) a8 ?! ?
// 创建一个按y坐标排序的点数组0 ?, C: b- Q: Y7 `% v& t
' a) j; P9 @* _' u7 d
Point2 *Y = new Point2 [n]; / p! W, k9 {9 J' Q+ Q1 C+ h# F & T% v$ x" b1 e1 P8 Zfor (int i = 0; i < n; i++) { / R0 C8 j. c) t0 ~7 n7 E0 {" v: R* \- V2 D2 B( ~& C
// 将点i 从X 复制到Y # V) L: D& m* P2 l) G: M0 `! L2 d- v4 A$ N4 J
Y.p = i; L: Y9 y1 s% s- S6 v2 ~/ |# j
9 c* k, @2 u; a1 A3 @: ZY.x = X.x; ' P6 r6 q! S: Z Z8 u) J' k {& F, j3 I+ o- |- \) r J
Y.y = X.y; / p8 R5 g* J8 X S$ ~: f6 L; D A A% h6 J6 y$ e# ]
} & z/ @% u) o+ U- X9 u 8 ?; _! a$ i8 O, b/ V) S0 FM e r g e S o r t ( Y,n); // 按y坐标排序# e; e$ S, T, y& v( ]
+ r8 T9 _ F8 \3 H* S1 o
// 创建临时数组 3 b: S8 o4 M2 S6 s4 l, | v M4 e7 x s: X" ?2 Z
Point2 *Z = new Point2 [n];% w8 d t# K% M' m' }
9 a: W' p* l! a, }4 r3 v
// 寻找最近点对 ' f* A! m% J' Z w/ f! q5 Q j) F4 e% M4 {
c l o s e ( X , Y, Z , 0 , n - 1 , a , b , d ) ; 0 b/ g. |# D% ^' ^ # C6 q* H6 e$ R l! \// 删除数组并返回1 S+ Q3 F& E1 A. B/ G- L6 A, Q4 H2 ]
7 Y, }* ^3 Y/ f; M6 w
delete [] Y; ) E: h' j3 K+ K7 P& I7 G# O# `! ~" K' `* G6 ]4 b! |5 `: }
delete [] Z; 3 G7 Q6 t0 f7 E6 ]* \( X" U' g& @. t+ ?: M- K( |
return true;! r3 q( p& x1 z
, E9 _; h5 l! s( @" x9 y% l} " p+ P0 \* D, v" a' z' N' J, x, z- G. o/ B U
程序1 4 - 11 计算最近点对 ! g: r( P' z1 n 3 c% z- T. G" T& j* H6 ~. Ivoid close(Point1 X[], Point2 Y[], Point2 Z[], int l, int r, Point1& a, Point1& b, float& d) ' N2 l0 V4 L+ w, g2 r# A2 p9 f( L) m2 Y/ U+ B. @! A
{//X[l:r] 按x坐标排序 % i6 l# q7 ?& y3 x) a7 m: v7 `/ O% ]2 N/ Z
//Y[l:r] 按y坐标排序 4 `& ?) y: t) I6 I: D6 ^! ]( Q! J6 c* F7 G% Z
if (r-l == 1) {// 两个点/ K. _3 J: L2 J# a
4 N% ?/ t: J6 @9 |+ {; Y! |7 ia = X[l]; 9 G. n6 V% R' a$ _9 P. \( y, N0 j: V! @$ _5 o4 {# e1 N
b = X[r]; . _. X. Z9 c: Q" {0 M( a* I; L8 j7 M- Y7 l0 Z5 X2 w$ t
d = dist(X[l], X[r]); 7 i3 a! D6 y2 J4 e0 E1 [. x7 t 4 Y9 \5 M, G) x ^) cr e t u r n ; }/ f$ n+ s8 y# ]9 l U7 k9 e' j
1 ~" p" Z/ D: D% Oif (r-l == 2) {// 三个点2 c% `( L9 E, O1 N: m
/ C! Z6 {6 `2 A. y0 a5 C2 W8 w C// 计算所有点对之间的距离 3 f+ A3 f' m7 ^ 3 r N/ A0 p' O1 i/ o5 a' B5 pfloat d1 = dist(X[l], X[l+1]); - X' b7 C, c- _: G" j' F 9 r6 r8 C: H6 a" E4 S: pfloat d2 = dist(X[l+1], X[r]); ' M$ w. \0 N, l8 y% t - L- t- ]. ~# Y- o& Kfloat d3 = dist(X[l], X[r]);% M6 }) x. _6 ~ ~5 R
2 E6 {) _1 z1 a$ x- N6 Q& w( ?+ _d = dp;: W% i) d C; h# a h. I1 o
' [! B- q# A. b! z% l
a = X[Z.p]; 4 |- T3 R# [6 v/ G) W4 y6 X u! K7 _+ K- s8 i, L. X2 c4 u% t3 s8 Xb = X[Z[j].p];}3 v9 ~$ |1 v: r# D
" ~& ?: r b& k} & v3 L3 b: y' {6 E ! B' G% P3 E, {+ d0 e* V, q函数c l o s e(见程序1 4 - 11)用来确定X[1:r] 中的最近点对。假定这些点按x 坐标排序。在Y [ 1 : r ]中对这些点按y 坐标排序。Z[ 1 : r ]用来存放中间结果。找到最近点对以后,将在a, b中返回最近点对,在d 中返回距离,数组Y被恢复为输入状态。函数并未修改数组X。: i7 ]0 k3 C ^; L$ E0 h/ n
0 n: y. S' q7 y# H首先考察“小问题”,即少于四个点的点集。因为分割过程不会产生少于两点的数组,因此只需要处理两点和三点的情形。对于这两种情形,可以尝试所有的可能性。当点数超过三个时,通过计算m = ( 1 + r ) / 2把点集分为两组A和B,X [ 1 : m ]属于A,X [ m + 1 : r ]属于B。通过从左至右扫描Y中的点以及确定哪些点属于A,哪些点属于B,可以创建分别与A组和B组对应的,按y 坐标排序的Z [ 1 : m ]和Z [ m + 1 : r ]。此时Y和Z的角色互相交换,依次执行两个递归调用来获取A和B中的最近点对。在两次递归调用返回后,必须保证Z不发生改变,但对Y则无此要求。不过,仅Y [ l : r ]可能会发生改变。通过合并操作(见程序1 4 - 5)可以以Z [ 1 : r ]重构Y [ 1 : r ]。 # s+ J2 @# c5 K3 w |6 p5 j2 P
为实现图1 4 - 1 6的策略,首先扫描Y [ 1 : r ],并收集距分割线小于的点,将这些点存放在Z [ 1 : k - 1 ]中。可按如下两种方式来把RA中点p 与p 的比较区内的所有点进行配对:1) 与RB 中y 坐标≥p.y 的点配对;2) 与y 坐标≤p.y 的点配对。这可以通过将每个点Z [ i ](1≤i < k,不管该点是在RA: D! D, C) R$ p/ ?! E% `9 R
( \) | \ v5 h
还是在RB中)与Z[j] 配对来实现,其中i<j 且Z [ j ] . y - Z [ i ] . y< 。对每一个Z [ i ],在2 × 区域内所检查的点如图1 4 - 1 7所示。由于在每个2 × 子区域内的点至少相距。因此每一个子区域中的点数不会超过四个,所以与Z [ i ]配对的点Z [ j ]最多有七个。 . |8 }! \: Z# r5 |3 b7 {' \# d2 L! @5 k, d
2. 复杂性分析 4 `4 p2 ]6 I/ e3 h( _* c0 ~5 V6 |
令t (n) 代表处理n 个点时,函数close 所需要的时间。当n<4时,t (n) 等于某个常数d。当n≥4时,需花费(n) 时间来完成以下工作:将点集划分为两个部分,两次递归调用后重构Y,淘汰距分割线很远的点,寻找更好的第三类点对。两次递归调用需分别耗时t (「n /2ù」和t (?n /2?). 5 Y% l0 ^) b9 ^ + U$ W3 Z& t- Q4 O/ _这个递归式与归并排序的递归式完全一样,其结果为t (n) = (nl o gn)。另外,函数c l o s e s t还需耗时(nl o gn)来完成如下额外工作:对X进行排序,创建Y和Z,对Y进行排序。因此分而治之最近点对求解算法的时间复杂性为(nl o gn)。</P>