. n; L: e2 s3 ~3 W' h0 Kp u b l i c : ' n4 c8 i) ]' `. }* S) J 5 S* ~ i8 r$ r+ y' l, Bint operator<=(Point1 a) const # T; z, X: `4 j( T' X M; a; f: d . d+ ?& B& O9 B3 i+ Q+ T( k q" `/ ]{return (x <= a.x);} ' y3 k9 b: y& m6 F ?4 z3 e6 g" a7 K* Q4 g4 f2 Z
p r i v a t e :! F7 X& I' p& h! B: _
i# \" l* I" U1 aint ID; // 点的编号9 ?, D+ {3 Y7 S0 o# f! `7 Y& g
9 i8 r7 C; t/ J+ c6 L. Q3 g6 a
float x, y; // 点坐标* B& J8 j2 u4 O" C9 ?' q
! N9 Y' N. `* B# B+ Z
} ; + |, A& F+ ~/ M& [# ]$ R9 X! p; c* }0 F, p) d* X9 u2 Z9 R
class Point2 {5 R B* S$ q6 e% _3 A
7 \8 S; R) Z! ]7 r6 E8 E. x( {+ Mfriend float dist(const Point2&, const Point2&); % v' V6 @. ]) T A8 F' _% h4 H& W2 V) n+ k
friend void close(Point1 *, Point2 *, Point2 *, int, int, Point1&, Point1&, float&); 3 |* T3 h. S8 N+ T, j0 M/ C$ T) D+ A
friend bool closest(Point1 *, int, Point1&, Point1&, float&);1 s- Z1 p& N4 u# z) k' G' N; D6 h
% u2 o! |; O) R* ^
friend void main(); 5 P. i# r/ [: {- p& _" u' a3 ~( L- `4 _4 }, w; P+ M
p u b l i c : {% V$ j& S6 p6 [# _. u' i8 k2 b; R {0 |
int operator<=(Point2 a) const # \' B1 ]1 l0 S$ } Y 3 y* \( t3 {7 v- d- k2 ^' N{return (y <= a.y);}9 H5 w* f" O, G. d4 z/ g
0 }4 p5 L0 u1 g6 T
p r i v a t e :& M& h2 ]! v o
9 L; ~3 d7 L4 ^! W5 d, a( L. y
int p; // 数组X中相同点的索引 ! A0 O" J, g5 ]0 F: i3 d/ {. N/ U. g" O e2 o& g( a4 k
float x, y; // 点坐标 5 H: ^: n& S& B! d% I* x" r+ L2 k! Z# M0 \! A5 |7 d6 K
} ; / R: d* C4 H" c7 Y5 C3 |0 L$ r" @: N# V6 B! V4 J/ @
所输入的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中的对应点。 ' w& ]; r; n- d" h& l0 z9 ` $ V8 U3 R8 g# d- v, Q. m& Y确定了必要的数据结构之后,再来看看所要产生的代码。首先定义一个模板函数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类的友元。7 ?& n2 z; l4 w/ b; S9 f# x6 V
8 ?7 Z1 z4 |2 N d7 @6 kfloat dx = u.x-v. x ;3 V9 H4 m. O+ J
% q3 F" X, U- e* V! H" u6 \
float dy = u.y-v. y ; " y6 N- L3 l# r5 ?4 k! I7 o1 c. k; M. G& k4 A# ?7 S
return sqrt(dx * dx + dy * dy);& F5 r0 \/ o+ W' X7 ~! v
$ D; [) o" g, _$ ?# O
} ' t, v8 p, w9 z( Y5 d6 L. k6 E ) W# n3 z: E8 ^2 K% a: R" g5 u如果点的数目少于两个,则函数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 ),该函数实际求解最近点对。: d/ r8 l& ~6 r+ i2 z! M
5 F% E! A$ C6 ?( K/ _, v- i
程序14-10 预处理及调用c l o s e , A2 Y5 e$ E3 C- M$ M& w# X0 Y8 E; i5 g' l: O8 M+ A$ z5 A
bool closest(Point1 X[], int n, Point1& a, Point1& b, float& d)0 o; l% _, O2 O M$ @7 {* [
) r6 w% o. U4 p, f{// 在n >= 2 个点中寻找最近点对, |7 }9 b I' {# n7 Q
$ d1 A4 H1 g& p8 v- ^5 f9 |
// 如果少于2个点,则返回f a l s e2 [% O3 R5 n: ~$ S* Y& t1 `( y
0 H% r }$ j/ ~1 k8 ~/ k// 否则,在a 和b中返回距离最近的两个点7 q5 N/ |& n' f; C& h m% p5 H4 J
6 y$ I# w6 |' i* hif (n < 2) return false;0 v) v7 b: o @0 v; V0 N: O* Q
7 d; N' O6 O/ e& ]+ _0 c) E" f
// 按x坐标排序 * W: n. \1 g5 X2 b3 p2 R# p6 H ! `9 S* ?& ?3 r& `* y& bM e r g e S o r t ( X , n ) ; 0 G0 B6 U) o& @3 I' N. ~" _% J3 S/ u ; y0 h+ X" j' Y' n% a// 创建一个按y坐标排序的点数组% b; w" D% U4 i& G4 K. L% m
" [, C! Y. W7 [a = X[l]; 7 }/ G% L6 b& U 5 ?6 W) q! F% E/ @b = X[l+1]; 3 R5 l c) g* e0 Q' k, i: h' ^4 R0 d' V9 h Y+ ]
d = d1;; c3 l6 J+ Q* J/ S2 F
$ x3 K4 w& @0 P$ `7 m
r e t u r n ; }9 d: ?, F0 E% k1 |& y
1 S% `2 Q/ F+ z$ t1 _. r+ G
if (d2 <= d3) {a = X[l+1]; 7 q# ]" t' {3 q. b2 n. u , N$ V9 q( q/ W4 }' i+ ~b = X[r]; 2 y0 t% g$ J0 x: O3 @# P" \4 u- }; \ H
d = d2;} ; R0 l& K9 E1 J4 V3 r : n5 E) U& }. Z2 \: Velse {a = X[l]; 4 l" w) m6 J; f- u. B& a: X) A; z, \
b = X[r];, ^6 N" F6 u4 u3 ?8 G" n# h) Y
3 e: a1 i6 E) r) |$ g3 c% ^, @" Id = d3;}. v7 R$ e' c9 W, v1 A
+ J- H2 m- \) N0 @: m7 Cr e t u r n ; } 6 F: k9 M J$ l; ]- k h" D1 H) O! V! O! f1 k/ /多于三个点,划分为两部分1 Z+ K9 f; @) }; ^9 ~) Q
8 b1 s4 \: w1 a9 _1 x1 Eint m = (l+r)/2; // X[l:m] 在A中,余下的在B中 ( c5 D5 |4 Q- Q+ G' h5 e 5 y M1 p) ^& p+ P- {// 在Z[l:m] 和Z [ m + 1 : r ]中创建按y排序的表0 C( r8 Z1 K) ~; t1 L% {) K2 K, ?9 X
3 e; ~4 g" V& }% y4 z
int f = l, // Z[l:m]的游标 4 S% m9 ?3 c* I+ K4 l: p: q6 _, R d4 X/ Y4 V
g = m+1; // Z[m+1:r]的游标 ! X6 s9 H% Z* c: w' Y4 X9 C + D& ]0 f Y6 P9 ~/ ufor (int i = l; i <= r; i++) ( L4 g2 K! Q4 _# a# X' ?+ p, r6 i; u' s& Y2 r& c: I ?
if (Y.p > m) Z[g++] = Y; $ t8 x% z9 E' U1 A1 g8 \3 K/ h! N- A 8 b0 [2 E5 U- Oelse Z[f++] = Y; $ u2 ^. o! Z8 T9 X0 x. B0 j, S1 y, Y# {# l' E/ k- f" |
// 对以上两个部分进行求解 9 A! u2 [. }9 F/ t6 h & L4 M) F5 `9 j& w4 Sc l o s e ( X , Z , Y, l , m , a , b , d ) ; ) ^+ [7 b2 `" R( W7 X; j0 F1 v& Q" g @$ N# N6 M. N7 u* N8 [: Afloat dr; - j4 a" e3 Q% U4 W8 r4 E, a/ w3 D 1 ^ j1 x; {5 k$ M3 F, {Point1 ar, br;5 Z* P( P% x) F% K% \( U
3 P2 _, d& ]% E% g3 Jc l o s e ( X , Z , Y, m + 1 , r, a r, b r, d r ) ; * R# V! ]2 C8 `6 r$ u8 e. m% M' r- z! L/ R& @0 z" V4 z- g* A9 K" n% i
// (a,b) 是两者中较近的点对 " L' J5 v! @/ J m8 Y3 \- h- s! y/ ?7 x2 K
if (dr < d) {a = ar;( `' D4 ?! {3 X: ?& ^
" a( ?) x9 l( {; W# K
b = br; - ], }6 ?/ I6 ?* g ) a- p+ e. Z4 \3 X# `3 u8 Ud = dr;}% ?6 H" m; D" O7 r3 V
8 T3 v$ C" j- ?, |. Z6 h% Q9 Y- Y
M e r g e ( Z , Y,l,m,r);// 重构Y: M! L/ D/ W! t9 Z+ c- ~1 W
3 G$ s1 a. y* D7 U1 _/ /距离小于d的点放入Z ( F3 m; N9 D$ k* V 5 \: \" V) N' h. _/ nint k = l; // Z的游标$ V% ~7 T6 I# X1 J, A- F1 o. l
3 o6 ~ P, d( u- f- zfor (i = l; i <= r; i++) 5 _& u0 a6 ~$ X0 d% `. T ~ _) g$ p E: `$ w U) d
if (fabs(Y[m].x - Y.x) < d) Z[k++] = Y;% V) v0 u, y% i& V
7 c3 M1 m# f; L. e L" w// 通过检查Z [ l : k - 1 ]中的所有点对,寻找较近的点对, m0 h4 R# R8 j. }
& R. `. P P8 e5 p' [for (i = l; i < k; i++){ 0 m: |+ x8 A6 D6 @5 ?. G5 N C+ f7 G' e% S4 l7 }for (int j = i+1; j < k && Z[j].y - Z.y < d; 7 y1 v- |( K8 S & r+ f+ [+ C* R3 i7 w3 Zj + + ) { + B7 B5 d+ @8 z$ @ - H+ ?' a7 e" u8 zfloat dp = dist(Z, Z[j]); 4 d! ^- u; s2 Y. z: ^2 s; j) y4 @1 ^( ]& A+ ~
if (dp < d) {// 较近的点对+ u2 c7 b( Y j* p2 Q: R
5 f9 l y& ~" D$ d, C/ ma = X[Z.p];5 N9 S; W9 H. V" r9 T, r8 Q4 t
6 P6 l# o+ Y F6 y
b = X[Z[j].p];}9 F. v% L& Q7 k9 ^2 F
* m' p# A- S" `
}4 S2 Z8 L0 x" f h& ~; Q+ h
0 A" e. g* T, o}8 W% f! n, }2 H4 b
2 y7 R/ b$ H% }$ U" z: l% O
}6 r& b( J/ x9 J
/ N2 P5 y0 d8 T3 r2 M6 j- m
函数c l o s e(见程序1 4 - 11)用来确定X[1:r] 中的最近点对。假定这些点按x 坐标排序。在Y [ 1 : r ]中对这些点按y 坐标排序。Z[ 1 : r ]用来存放中间结果。找到最近点对以后,将在a, b中返回最近点对,在d 中返回距离,数组Y被恢复为输入状态。函数并未修改数组X。& d3 \: ]$ _# U% S! D
P o1 k& j% I7 C3 E首先考察“小问题”,即少于四个点的点集。因为分割过程不会产生少于两点的数组,因此只需要处理两点和三点的情形。对于这两种情形,可以尝试所有的可能性。当点数超过三个时,通过计算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 ]。 # \7 L: G9 r' A' z7 B, U0 ]5 [) ~% }1 } V
为实现图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( o2 w( i( f9 B: j
0 S7 q: B2 Q+ i1 ~! I还是在RB中)与Z[j] 配对来实现,其中i<j 且Z [ j ] . y - Z [ i ] . y< 。对每一个Z [ i ],在2 × 区域内所检查的点如图1 4 - 1 7所示。由于在每个2 × 子区域内的点至少相距。因此每一个子区域中的点数不会超过四个,所以与Z [ i ]配对的点Z [ j ]最多有七个。 ) H" B9 l8 K- h# C! R- A! F7 W % @( H% A& v, b) N) j1 r2 F2. 复杂性分析 $ c5 p9 y" b7 a$ L7 V3 Z j" e' E s5 ^1 c
令t (n) 代表处理n 个点时,函数close 所需要的时间。当n<4时,t (n) 等于某个常数d。当n≥4时,需花费(n) 时间来完成以下工作:将点集划分为两个部分,两次递归调用后重构Y,淘汰距分割线很远的点,寻找更好的第三类点对。两次递归调用需分别耗时t (「n /2ù」和t (?n /2?).% }0 W4 a. O1 b) c5 \
, t) v# a3 F: U @
这个递归式与归并排序的递归式完全一样,其结果为t (n) = (nl o gn)。另外,函数c l o s e s t还需耗时(nl o gn)来完成如下额外工作:对X进行排序,创建Y和Z,对Y进行排序。因此分而治之最近点对求解算法的时间复杂性为(nl o gn)。</P>