: ?2 ?# M$ Z0 a7 @9 D6 N: f1 {class Point1 { 7 g3 b0 ?5 a. ~2 ?' g F, J - V H: ^) e" X8 Bfriend float dist(const Point1&, const Point1&); : h s, s7 `$ V" P6 e P4 x9 b0 Q' E; P+ l4 S
friend void close(Point1 *, Point2 *, Point2 *, int, int, Point1&, Point1&, float&); & U/ H! y( `- e4 Y7 t: K! n, u7 R5 z
friend bool closest(Point1 *, int, Point1&, Point1&,float&); ( F! h W% Y( y3 K3 w 7 N a' Y, X( Y/ Nfriend void main();# a4 P% T/ l1 T& C3 ]5 N
6 L1 t1 p7 z# r/ V: P9 F; W4 Xp u b l i c : - }+ ^3 F X4 B $ ~$ _4 L& O% rint operator<=(Point1 a) const% Q: |2 S& d8 `4 W
4 b) N! s2 t: X3 i1 _$ ?
{return (x <= a.x);} ; j# a# N5 h% g, P 3 }" j( }6 s- q7 t8 bp r i v a t e :! L# J N2 m, B; r) U4 O7 v
% y1 V% ^/ A* N6 g
int ID; // 点的编号 2 |& b# X- c3 U! I& @% ~2 F : |% C: f" q" ?float x, y; // 点坐标3 L# t. N# N$ Q- p
7 m: j8 q. s% m" Z2 o, ~- @} ; - z. |! b6 Z7 a ! p( Z2 M g3 Hclass Point2 { 0 s# K. Y3 T4 ]; K! {! g `. r' e1 }7 `! ^
friend float dist(const Point2&, const Point2&);0 u6 N- [1 q* x0 f! \5 G
5 j0 ~7 C* S, L/ E& ]p u b l i c : 5 w/ j5 \$ N: X9 i0 F3 V! `3 e* `: a: o! b) y
int operator<=(Point2 a) const 9 I2 W, R1 m2 L# C3 r9 ~/ F+ n$ C5 R8 L. Y# A3 @
{return (y <= a.y);}* }' u/ m' H- N
8 j; T% s7 a/ W
p r i v a t e : 1 @8 N8 ~ \# C 5 O) W2 u9 T4 K5 {/ xint p; // 数组X中相同点的索引 3 ]% s0 i" D3 j7 U6 w 7 Q) I* }5 C1 |5 s4 u2 l1 F, u; V8 Jfloat x, y; // 点坐标( h8 z! m; e8 } O
; q1 B- ]7 E5 f1 B3 [} ; 9 o' t: {9 r2 J6 p# m3 J/ m , x: G* Y0 |1 H8 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中的对应点。9 V7 T# v( U! b0 I' H( `2 }' p
1 D3 y, O7 [( C8 e. R/ d确定了必要的数据结构之后,再来看看所要产生的代码。首先定义一个模板函数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 Q$ P- ~6 p& H( v, x5 A' a
# z" a! }" _* \& Y; Q# ~$ w7 Y. n: [float dx = u.x-v. x ;( A/ R( @+ d7 O0 {# n
5 D( g) O9 y2 O# t, S7 Q
float dy = u.y-v. y ; 7 N; b% i7 C$ c) C6 q0 \: s' p! J% J" p( A7 a: T
return sqrt(dx * dx + dy * dy);7 @" x! E5 G( d" \- v0 q: d$ A
% J/ e, @1 [! T}0 T! ]6 _# |- e3 y
u2 p2 }, K9 }$ O/ ?* O- c$ U2 q如果点的数目少于两个,则函数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 ),该函数实际求解最近点对。 ( @$ v' @4 C! n8 M9 X2 r. z$ U 5 p1 a$ w$ A4 e2 w6 {" k程序14-10 预处理及调用c l o s e 2 [) R: p: t: w! L- e' z) V l8 W" W& g' [" ?0 q$ n8 g& Tbool closest(Point1 X[], int n, Point1& a, Point1& b, float& d)! r2 k/ b% a9 Z6 E
( M$ [5 @( S+ l6 H{// 在n >= 2 个点中寻找最近点对% V! `2 u2 a0 z# F
, @$ F7 W- m6 ^% G$ Y
// 如果少于2个点,则返回f a l s e& `( `! m3 ]1 y0 ?7 U! D$ y% s
% |' w) @* t! G, N# {8 J' _// 否则,在a 和b中返回距离最近的两个点 5 I7 A. {2 O2 U- q7 T0 u & y9 u0 G7 t. k2 H9 h. X5 N' Jif (n < 2) return false;3 N' J' K0 ]5 y0 t: j: T' @
4 s/ B( p6 f2 N// 按x坐标排序 4 B- X: r3 ?/ |) J4 k) n ; r& _ P+ j. R+ CM e r g e S o r t ( X , n ) ;! B) X, N8 b5 a5 z2 H9 w, _. u$ w9 P
1 N8 G& j) J2 t8 x' ~Point2 *Y = new Point2 [n];+ n( B- j5 T# ]+ K3 z. O
. p7 Z7 l6 n5 }- H$ l! e6 bfor (int i = 0; i < n; i++) {; T: r* G7 m; }) G8 b- k4 ~6 E
, ? g# O% n0 j9 }9 i6 @
// 将点i 从X 复制到Y - g1 `8 P9 j; x/ A' `, o3 l- e # ~, \) U/ D1 KY.p = i; 9 {2 `; L+ W6 X1 }7 L; h' C% n, R' G! S% a1 X1 a2 y
Y.x = X.x; ! n$ K+ O+ M, K2 r7 C: A5 i3 ^7 S, c t0 u7 J6 Q+ U
Y.y = X.y; ! k" F5 {( U8 |! G' r" L7 p* u ; s9 `; O' g7 I. F1 K( B$ c} 3 g- q2 Y! O d& ^! m' j ) x( F- Y+ w) KM e r g e S o r t ( Y,n); // 按y坐标排序% ?/ E4 U5 C) D4 k0 @
; g2 I. g3 O. f) w8 x$ e// 创建临时数组3 P! X: } c3 b/ Z3 v- z1 I3 Z1 A6 ^9 A
# Z! B/ @9 i1 \- uPoint2 *Z = new Point2 [n]; * X. m/ M4 n$ n. E6 S ' `7 ~7 g8 t' v! W6 ]+ ]// 寻找最近点对% u# {* @. H' O
) g" _ N9 A7 ^1 f, F+ j9 C3 A& Oc l o s e ( X , Y, Z , 0 , n - 1 , a , b , d ) ; / e1 j$ V6 J- B( [2 {. ? " U9 t, \1 s Y9 C" o; T// 删除数组并返回 9 i3 K( M, I+ P( `" f1 O: E" g' t6 @1 p- v
delete [] Y;2 l9 L2 ^9 m# e7 I. _& I- Q6 g
3 @* C) T) T0 [程序1 4 - 11 计算最近点对 : h& P9 n$ u, ?2 N7 x6 _: F ( O. S/ B9 w( ?/ S6 avoid close(Point1 X[], Point2 Y[], Point2 Z[], int l, int r, Point1& a, Point1& b, float& d) - ~' x+ ^/ u f& m! \; e- R0 O9 h5 s1 J7 } U$ N1 B3 J2 a: Q
{//X[l:r] 按x坐标排序' b. b- u% w% D& D, S K- l
1 {# u/ }) z6 q2 [5 \: Y//Y[l:r] 按y坐标排序4 l8 W: o( P& S$ t/ E% I/ \1 h
1 z' `5 N5 q8 j7 e' t( }2 Y
if (r-l == 1) {// 两个点 - e7 ~9 [3 r2 b* }+ ]' T 8 V- i1 G# W5 J( m' u$ K9 e% a: aa = X[l];2 m! h. m0 R2 i% D3 u1 L
1 B1 Y1 s0 {: N' R. I
b = X[r]; 8 I3 N! u! N. I$ l4 i3 I : u$ Q b# K; f# @d = dist(X[l], X[r]); f( H- r m$ W: q1 ~, s$ f8 j & T8 F( z6 M5 m5 I1 [/ [r e t u r n ; } R6 |. \: {2 p2 ^9 j) d% P1 G0 G3 G" L& j2 \
if (r-l == 2) {// 三个点+ ^. @& X9 T5 b6 ^0 w
* }; V: d' |4 y& W' c' Y' i; L/ z& R// 计算所有点对之间的距离 7 E5 s* i4 l7 E" ~6 x& e- A W 7 J/ x c4 v# x9 @3 u& u: Lfloat d1 = dist(X[l], X[l+1]); ! _9 n! j6 d' B: `& i 5 Y. A2 F* {7 g R m; o+ L% Z: kfloat d2 = dist(X[l+1], X[r]); & s! Y* @, \7 P6 P7 ]! S) w5 j& Y* n0 q: x
float d3 = dist(X[l], X[r]); & D3 M8 r% c2 N+ [5 w( m # g3 b3 l) ^- i% W// 寻找最近点对 ' z; l; I; l. A( J; @: L / u6 T8 Q8 V, z: X/ L2 Dif (d1 <= d2 && d1 <= d3) {& n+ C- p; m8 Z3 S G; r
8 y0 f4 `# I; g: N, H/ Xa = X[l];& r- y" G+ Q6 V: l9 g' l7 T
2 Y( ?. k2 }2 w$ Q$ S2 n
b = X[l+1];' K; _: i! c7 f" r
/ r6 c9 @) v2 q6 R$ l7 X: R7 Yd = d1;. u/ z1 d+ f7 M; z/ J% |1 b
4 W% P K$ P. r" n! L
r e t u r n ; }! Y- Y" [# J+ x" Z
^0 R: U% k* }' g' Y& T
if (d2 <= d3) {a = X[l+1];3 \, |( o! t' c1 i" m7 p2 Y
/ R- a1 Z1 Q/ a, h& f: |# F1 V9 mb = X[r]; ) ?% w' P5 e" p& W: g5 n1 F9 _9 E% _' w& G
d = d2;}/ q0 I. |& C. V
9 j! z1 \1 K4 {0 k* v v& [- ob = X[r]; 9 }# X7 E- `5 j8 ^1 j! c * x. S- p, N9 j+ s' g- md = d3;} ( u" p1 s: q& K* o6 b& K 6 q! ]( d/ O0 G# Ir e t u r n ; }1 J) Q' m( L; h' ^$ J6 o
( @ h% |2 D3 F! W, ^
/ /多于三个点,划分为两部分 " U% k/ Q) S. g 3 T) M j, A' k. @- Yint m = (l+r)/2; // X[l:m] 在A中,余下的在B中* p* L: k1 D G& X
' a5 `# g, v7 z* n1 X// 在Z[l:m] 和Z [ m + 1 : r ]中创建按y排序的表1 j! ^8 h4 y4 K
: G& c; U1 o, N6 V ^( `3 X. Y
int f = l, // Z[l:m]的游标$ O/ {$ b" s7 O$ \) N5 d \
' t6 `5 L- f0 p, S: }! M" l; L1 R' Ffor (int i = l; i <= r; i++) ; t1 c* V1 L' R2 s T9 a& G7 w, w
if (Y.p > m) Z[g++] = Y;% ]' b8 t0 _& L5 f' i
' I* U+ ~ ~- O; a, n1 B
else Z[f++] = Y;' V0 H2 T; @: ^
) t6 ]& ]7 @ J- n$ L
// 对以上两个部分进行求解 1 w) t9 [, K" [5 c( K + e. I% Y$ Y! S" yc l o s e ( X , Z , Y, l , m , a , b , d ) ; 9 Y- G. {- h& ?' q" Y' w 0 r4 L! I% U# V6 [& h. Tfloat dr; 9 z1 v v O, K" l* W1 \8 n' b9 p( P9 B; n* r7 m5 j
Point1 ar, br;0 [) ^$ r' q# `0 b
4 F! i6 _: l) i6 o
c l o s e ( X , Z , Y, m + 1 , r, a r, b r, d r ) ; 0 x: K! A& W# `; _' G K0 y7 H( x; v0 F# u7 l/ n$ l
// (a,b) 是两者中较近的点对 2 F* v" z! o) j' h# X7 [; i$ \& H8 B" g
if (dr < d) {a = ar;/ z. Q& a$ m/ ]* o: [
+ I, H' U7 x; ]* {9 ib = br; ' y7 E f* {& }. `9 ` & ]# k. Y% N% B+ w# Dd = dr;}+ Y8 |$ X# I3 K3 a
" T9 h1 @ s- c3 {6 nM e r g e ( Z , Y,l,m,r);// 重构Y5 _2 K/ Q, f/ u8 w7 }' Z
$ u, C f" O5 ]/ /距离小于d的点放入Z o# p) T4 a$ N# m i % P: a; v# D) v. P' fint k = l; // Z的游标1 z2 g6 K" C" L/ {) w/ m% l* w
- G& s! y! v; M7 B |6 Z% U
for (i = l; i <= r; i++) 0 k$ T/ ^! F4 l$ M, [6 c e! m + Q# J) n$ Z( `) \* Xif (fabs(Y[m].x - Y.x) < d) Z[k++] = Y;5 D" h* N% h; `1 g T7 k
- r& a& k8 C }* }( T
// 通过检查Z [ l : k - 1 ]中的所有点对,寻找较近的点对) Z8 W* |# p- T- n: ]1 e8 k
3 d1 B+ f6 |2 u
for (i = l; i < k; i++){! e) W6 x& v# u" {
0 R3 t7 a8 A* f" e. r" M
for (int j = i+1; j < k && Z[j].y - Z.y < d; / V$ C" a `) N1 m5 a% }6 d3 h+ J% g3 e3 o7 o
j + + ) {: D7 U" b( p" L* g/ O" R
3 Q. h5 G: E ]/ b' W7 pfloat dp = dist(Z, Z[j]); - Q0 Z3 i8 v/ n & @( `" V0 d5 r% _% H: `if (dp < d) {// 较近的点对1 P3 l% j# l% q! P
' ?) q0 K- l1 A9 W3 ?+ ~
d = dp;$ M) b; y: v3 b& |
! @3 M( F/ A. ~% C; D9 Ka = X[Z.p];4 y$ N+ W. A' c; j' p2 q
8 i: ?+ M5 ^4 |/ f7 v
b = X[Z[j].p];}. J+ X4 `& p* c w
+ p& W( @. t- ^! t( U0 [
}$ r% U# Q4 F% H* r5 D0 ^1 v+ ~
! [3 ` f* z; |( ^" t0 a
} 8 ]- ]* B) X8 g! |# V4 S p8 f! C. a J x: a6 m" s, ]}3 K; I( t' @( I! B8 r8 b G0 D- p
) P. J- X/ k. ]) _$ z3 E函数c l o s e(见程序1 4 - 11)用来确定X[1:r] 中的最近点对。假定这些点按x 坐标排序。在Y [ 1 : r ]中对这些点按y 坐标排序。Z[ 1 : r ]用来存放中间结果。找到最近点对以后,将在a, b中返回最近点对,在d 中返回距离,数组Y被恢复为输入状态。函数并未修改数组X。* S* S, p. b& r) K
8 G3 j6 J( g9 u9 X6 H$ 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 ]。' i) o; ~0 E2 g1 s& ~6 x
1 @: H7 E8 U, V% ~4 z为实现图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 $ A, p7 N. W0 z6 i k ; d2 m. G' f, X: T2 I, z# s0 C3 }还是在RB中)与Z[j] 配对来实现,其中i<j 且Z [ j ] . y - Z [ i ] . y< 。对每一个Z [ i ],在2 × 区域内所检查的点如图1 4 - 1 7所示。由于在每个2 × 子区域内的点至少相距。因此每一个子区域中的点数不会超过四个,所以与Z [ i ]配对的点Z [ j ]最多有七个。 . F& k) u. h$ Q / q9 h; g& |' m! K- \1 P2 a" g2. 复杂性分析 8 P: y; q& E8 ?# S; n. v& Y7 A/ X) _& x8 E+ z0 a. Y' [9 V
令t (n) 代表处理n 个点时,函数close 所需要的时间。当n<4时,t (n) 等于某个常数d。当n≥4时,需花费(n) 时间来完成以下工作:将点集划分为两个部分,两次递归调用后重构Y,淘汰距分割线很远的点,寻找更好的第三类点对。两次递归调用需分别耗时t (「n /2ù」和t (?n /2?).7 u% t3 s8 N/ l2 K' a
4 |8 g' ~) f1 x! V: d J这个递归式与归并排序的递归式完全一样,其结果为t (n) = (nl o gn)。另外,函数c l o s e s t还需耗时(nl o gn)来完成如下额外工作:对X进行排序,创建Y和Z,对Y进行排序。因此分而治之最近点对求解算法的时间复杂性为(nl o gn)。</P>