$ Q! Y- b, N9 Y$ v( r+ N" Z. W! U( D# W7 p# O
if (n较小) {用直接法寻找最近点对 7 S$ I0 K1 n q9 ]; }3 L5 S$ `$ h: ^1 m \* g6 w
R e t u r n ; } + n. ?2 H% R+ M4 a- p$ b r) D + y; W% L7 V2 u- V5 Y. T! e// n较大5 G% I* L: ~% }$ a# V
& D' e5 C. b5 [将点集分成大致相等的两个部分A和B 3 D4 g, V' H; n0 b6 P* J : H* [9 T9 Q( y3 ~: s确定A和B中的最近点对2 W" J. D! G* o. \' k2 A
( m, t: m. B1 ~- S9 X# C
确定一点在A中、另一点在B中的最近点对 ! I* u0 v$ k8 y# T+ m9 {) X/ Z; `+ \4 F4 T+ b F8 T ?0 E# b# l
从上面得到的三对点中,找出距离最小的一对点# j* \) k/ H7 o" i7 n
3 P' }, | N9 P+ o% Q5 fp u b l i c : + z* j' O' I( t N; ? . G8 R) R1 r3 h% o% @% t9 Sint operator<=(Point2 a) const4 g4 A8 v8 C4 s, \
2 A% @: b6 [8 H$ m9 w7 ?
{return (y <= a.y);} . Z, [5 V: _) c/ d; g4 z! W9 P p I+ P( {2 z% S* ~$ L6 c
p r i v a t e : 9 M" Z3 m# ^& }* V/ Q . D3 \; q. ] U- `8 Pint p; // 数组X中相同点的索引 . i! }+ p: z3 e- H% \4 o' E" A6 f; q) k" w% k+ w( V2 [+ i: [* x
float x, y; // 点坐标9 C4 F! h) r; K3 p
: q! C& `' F& Y# A7 _; K) s
} ;7 G. \- j1 I' p4 L& r* ^2 W8 }
9 ?! `' K4 K( n+ E4 U5 R+ G
所输入的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中的对应点。 - F l: D4 C3 m) y2 r3 D. C6 D3 f! |. U# i
确定了必要的数据结构之后,再来看看所要产生的代码。首先定义一个模板函数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类的友元。3 f6 D7 H9 { K! g8 O
9 r% s. f$ t8 ?- E
程序14-9 计算两点距离% _0 m3 E) J- S1 W. @1 D( q
6 e9 L5 x( z9 l8 c" e0 j/ S如果点的数目少于两个,则函数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 ),该函数实际求解最近点对。 " N( c6 B/ A# G2 V6 F( `& f% f# Z/ |' W0 p1 y0 r
程序14-10 预处理及调用c l o s e 9 H) i4 l0 {. J5 L9 E( J 7 ^7 K( a8 h% S+ N$ c4 {; H, e6 Z3 Fbool closest(Point1 X[], int n, Point1& a, Point1& b, float& d) & Q& g4 a/ x: i- o4 S" W0 I7 c$ _ . V0 q1 r" G8 x# C1 _{// 在n >= 2 个点中寻找最近点对. S% H% w6 T* h) @% q; C
) V7 Z$ Q- y+ j; k" e8 o
// 如果少于2个点,则返回f a l s e q. b0 M; K0 Q4 c1 m4 ^
0 U' k l! Q7 F8 S. J. \// 否则,在a 和b中返回距离最近的两个点 3 A" H/ f+ l6 F$ \7 ~; }4 p5 P. M9 X* U5 m
if (n < 2) return false; 9 r. }! Q) R4 l, ^1 L0 i" o, L R* I3 U& m
// 按x坐标排序 + V4 `! m7 Q2 c , g4 n {* K. q3 V5 C" ~: j' i4 LM e r g e S o r t ( X , n ) ; 8 v: i5 j' ~7 c" m, |& S( o1 [5 T6 |; R' X# v6 Z/ e
// 创建一个按y坐标排序的点数组1 z+ Q" D+ e1 s+ K
" k4 y+ s" l* K0 e$ m) U
Point2 *Y = new Point2 [n]; 2 \+ P `7 W9 I- y: i. v/ w# F5 c$ ]+ y3 m& m+ z+ ~# N9 B
for (int i = 0; i < n; i++) { 9 u, _6 X- F% o5 p; c; A# [0 z3 j: s# `/ q
// 将点i 从X 复制到Y . o: ]9 D& q9 I# a6 Z0 K( _& X! V }% i% f* N2 X7 f D8 R" W
Y.p = i;& X8 \; i7 o( X9 t- [: A
; C% }& }( q% LY.x = X.x; ' G7 a8 ^3 N. c% x2 I" I , I; p/ G! q) w% OY.y = X.y;% L9 @, c. n* D' }1 S
' o6 B) w5 z9 l" y/ z}9 e7 l: V$ }8 O" M
$ c$ p/ |6 M! m: r$ T
M e r g e S o r t ( Y,n); // 按y坐标排序 " b; N( Z- z, u* F: }8 `. [+ V, E- x! q* k. @7 b3 P
// 创建临时数组 8 A2 z3 R. k( c/ z4 s, v& Y$ _9 n$ L7 Y; K' F- i: ?; n+ Y& d" G0 S
Point2 *Z = new Point2 [n];" Q2 K9 g3 n! ~( s# T4 k' f
; E2 C- [1 P# Y) a0 J6 s5 J% p2 X0 O// 寻找最近点对 , Z( ?$ g4 M9 G3 b " ?/ M5 g4 K. T( sc l o s e ( X , Y, Z , 0 , n - 1 , a , b , d ) ; , i2 o, \0 I. v- U. }( w. [6 d# O
// 删除数组并返回 - m2 o4 s4 w; Q( f3 B1 E 3 o' Z8 q' Y( w5 t1 @delete [] Y;5 V+ b! L0 S7 l6 a$ {0 f
* h# G) K3 Q; E! Z7 f+ c) W0 A/ y
delete [] Z;* o0 F; Y, V( b& v) P+ }# ?
( ]& |4 \4 }6 W/ _3 v$ P- l2 Bfloat d1 = dist(X[l], X[l+1]); 5 e6 n& X! e0 [7 ]) }+ O' Y+ J, f1 e
float d2 = dist(X[l+1], X[r]);" `6 [7 [% \( _7 P- e
+ a$ N& K! z3 m Ofloat d3 = dist(X[l], X[r]);2 W& a! d: n; o7 _$ k
! ]# {/ z( z$ d7 N5 Z9 L// 寻找最近点对 2 q. a# p9 c+ y& b. F) W1 |2 W 3 u! S+ S7 |6 _7 Oif (d1 <= d2 && d1 <= d3) {5 c9 F0 K8 d6 g F
; u O' b8 D/ h. `4 D
a = X[l]; 0 f# d- B* t" v7 w# d) o9 h2 T! J6 U8 P$ B/ [" w* @ j$ r
b = X[l+1]; , m4 L: C% d- q7 W: W, o ' W; [. I T$ C6 Ed = d1; C# G- R6 s/ A$ j) D( U1 e
: `( U; a. V( X! I, @) e/ u3 m c1 ?& Jr e t u r n ; }" r9 `3 [4 W# \) G
+ R/ [$ V8 n; J! Wif (d2 <= d3) {a = X[l+1]; 0 K$ S( r' {7 ~9 ~+ r" J, J 7 h; e& q1 E" P( u- q: T( Vb = X[r];9 o5 U# l6 M% Q9 J) `
" C2 r* ^* O9 q5 y/ E. E( u0 Q
d = d2;} / \; P. ~1 B% f N ; w. i* _7 Z6 nelse {a = X[l]; 2 c5 S0 M }( ~* l6 n; q+ g$ k2 m( u+ Q' V
b = X[r];- p8 V- ?- O, O, b9 M" B# \
u' l+ ?: j: H4 q+ L
d = d3;}& f$ Z; a2 Z* u" d
4 h7 d0 H, k1 T% w. \! nr e t u r n ; }+ @3 A8 ]$ W8 E' z2 {; y3 s0 J% }
* @$ ~$ D0 ? M" }1 H* W4 @
/ /多于三个点,划分为两部分9 c/ h7 |) F9 B3 T; }, L4 w5 Y
4 `/ z+ c/ `/ W
int m = (l+r)/2; // X[l:m] 在A中,余下的在B中 ; a" S3 I3 D1 d' p, o9 e7 M/ |/ |0 Y2 a) \% Y5 f
// 在Z[l:m] 和Z [ m + 1 : r ]中创建按y排序的表# a' b, b; V Y& G. B7 E) ]! h, o& m4 i
( w8 G" C( t# T! r+ pint f = l, // Z[l:m]的游标4 ` K7 Q+ v2 P1 Z
! k' R7 F6 V# b- Q5 t) J
g = m+1; // Z[m+1:r]的游标% ^2 x# {+ J; r. D3 o1 e
/ [0 d# ]2 M" _- H) u' h& j
for (int i = l; i <= r; i++) + y& B3 V- ^/ g: @0 r 4 s: n# Z0 h# V, ?if (Y.p > m) Z[g++] = Y;6 q+ i8 A0 o+ L# J# u6 X
" I# l* j) N2 B
else Z[f++] = Y;$ H8 N% [& _/ X4 T/ A% q
( _+ @; s- ~0 B8 n% ^ h @
// 对以上两个部分进行求解1 k8 T( d' {1 e
/ x7 Z0 E8 }: Rc l o s e ( X , Z , Y, l , m , a , b , d ) ; g% V6 B8 Y; `# u# B' h, o! u / {0 F' C& R# h9 w) Q- i, b7 ~float dr;( V3 P6 g9 d& W; u( X L0 e
. L* S- t5 m- z$ n7 QPoint1 ar, br; # Q4 [/ Z" V3 o# k% G: z# n4 `8 a+ b2 a( M
c l o s e ( X , Z , Y, m + 1 , r, a r, b r, d r ) ;9 m3 e1 N# H* P: p; B' F% c K6 Q
% G3 T! z' Q) q3 ?3 l- U
// (a,b) 是两者中较近的点对+ u7 Q& E5 |# k$ L0 @0 F3 l' I$ h
" V& m# ^5 P4 c, W4 a
if (dr < d) {a = ar;- q5 D/ R' s1 N1 L
% ?9 C$ |! V. ab = br;; L0 H7 j, Y+ f2 n( G! ?1 g
7 F! S. B/ Y& @d = dr;} $ K9 _! E9 x% ^. p7 r& q* K9 R: d% ]) [ v) e
M e r g e ( Z , Y,l,m,r);// 重构Y: G* I9 ^$ ?7 W9 X4 W p) _ g
/ o) [6 t9 p b/ y7 [/ /距离小于d的点放入Z . p) V6 n& o* Z# U0 |! W+ m8 G0 ~% U 8 |* H" o+ i( S K; j Cint k = l; // Z的游标 - @3 X0 `/ G3 H- r( ?, b5 x ) ]! y0 `9 d4 |4 y6 r5 x6 sfor (i = l; i <= r; i++) 8 S0 A8 k7 E# v& D+ b5 d3 v [" P" C; z; I! M
if (fabs(Y[m].x - Y.x) < d) Z[k++] = Y; 6 Z2 j: v( Y. w1 U4 K9 }/ M1 h" [0 u' q+ Z- v7 d
// 通过检查Z [ l : k - 1 ]中的所有点对,寻找较近的点对 ! {& K" b$ T! l( W/ f+ _- ^7 y. w* \- b* M/ }
for (i = l; i < k; i++){ ' x4 {9 i+ X) F, T" J( K# V 4 @3 y' R6 f! o6 Q, x9 ]. H* `9 Mfor (int j = i+1; j < k && Z[j].y - Z.y < d;7 t) L8 {# e3 |7 G! \9 ?+ U8 F' K
8 y A* D: l! H: A tif (dp < d) {// 较近的点对, k: F6 B N0 N7 O
. b6 N3 o! b' f6 f" qd = dp;( H. s8 Q; t1 g$ {
/ y& U5 b' l5 l0 |4 ^- E6 B2 \- ?a = X[Z.p];# m+ r6 G5 k. V
9 Q& z% ~8 e, ]1 M8 h/ S/ ?
b = X[Z[j].p];} & C. H! ^. s9 |- g( `. |) d( W& |- i. V6 I" x. l2 K
} " v; p! E0 H% k; ]9 D5 Q( l 1 B; H( n6 T% ]} , s; E2 ?: Q. U4 _+ m! M9 ~) M# F3 R; J/ h$ l. U
} , o7 y' {5 M: x. j, f) q& e, @8 @3 u7 g( b* u/ B# o+ [
函数c l o s e(见程序1 4 - 11)用来确定X[1:r] 中的最近点对。假定这些点按x 坐标排序。在Y [ 1 : r ]中对这些点按y 坐标排序。Z[ 1 : r ]用来存放中间结果。找到最近点对以后,将在a, b中返回最近点对,在d 中返回距离,数组Y被恢复为输入状态。函数并未修改数组X。9 d' f& }4 \- M* z. Y
0 Z4 j8 F9 @1 k0 \1 l/ u9 r
首先考察“小问题”,即少于四个点的点集。因为分割过程不会产生少于两点的数组,因此只需要处理两点和三点的情形。对于这两种情形,可以尝试所有的可能性。当点数超过三个时,通过计算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 ]。 6 {& f. @: Z3 ~5 ?9 r; z+ c' a H3 A7 [% f" {4 @为实现图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, c, Y- C; \+ e8 T- C5 V' V
+ H( \5 |8 M0 e; I; r还是在RB中)与Z[j] 配对来实现,其中i<j 且Z [ j ] . y - Z [ i ] . y< 。对每一个Z [ i ],在2 × 区域内所检查的点如图1 4 - 1 7所示。由于在每个2 × 子区域内的点至少相距。因此每一个子区域中的点数不会超过四个,所以与Z [ i ]配对的点Z [ j ]最多有七个。 7 |* H# W; s! c7 c( z ! i, c+ t# _ N4 ]2. 复杂性分析! y$ ^9 `8 B+ j+ y
! t9 h' b8 I5 T2 u1 _
令t (n) 代表处理n 个点时,函数close 所需要的时间。当n<4时,t (n) 等于某个常数d。当n≥4时,需花费(n) 时间来完成以下工作:将点集划分为两个部分,两次递归调用后重构Y,淘汰距分割线很远的点,寻找更好的第三类点对。两次递归调用需分别耗时t (「n /2ù」和t (?n /2?)./ H8 m1 w+ d( n3 L$ O6 T
& k3 z( D& y: ]6 ?" [0 q这个递归式与归并排序的递归式完全一样,其结果为t (n) = (nl o gn)。另外,函数c l o s e s t还需耗时(nl o gn)来完成如下额外工作:对X进行排序,创建Y和Z,对Y进行排序。因此分而治之最近点对求解算法的时间复杂性为(nl o gn)。</P>