! U! f0 V. q1 a4 Lp u b l i c :' k8 Q1 s: K* K" L# c8 p& t, D
% d$ ?! g# W! g+ hint operator<=(Point2 a) const; L$ x& m- W( {# f9 }! k
) h4 F' i* K9 H{return (y <= a.y);} y1 W+ W9 @$ j _$ @$ H3 p4 Z4 v. g2 H3 S
p r i v a t e :, W6 q- z/ h; D F* J6 H
- _1 w1 I8 z' C) `6 T1 Gint p; // 数组X中相同点的索引 $ q! R7 S) I1 `0 [- f* f& M E/ W! Y$ @7 C1 { z
float x, y; // 点坐标0 D$ C- g: f0 g
! \/ g( B5 N& n
} ;+ O, e" x" }! e$ e
% c1 f W @: w: j6 f所输入的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' k' E! Y7 R" I7 ?& l 7 }; k1 [& Y. a确定了必要的数据结构之后,再来看看所要产生的代码。首先定义一个模板函数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类的友元。 . K' F! E+ }& u7 L* H* R4 r 0 i0 `6 t' t8 f5 v' X$ b程序14-9 计算两点距离 , f6 ~* x+ a$ |8 {* B5 V" ~* R& s$ b8 X" h5 `3 i) {, C! ~) g
template<CLASS T>' X: ?9 k6 ]8 f* M7 e3 l. e( A
) t% s7 \6 K$ p0 l, W' E4 R. L
inline float dist(const T& u, const T& v) E) r* M' s1 t1 D& n. B ) g2 G: l$ D+ z0 d3 Y* [1 S{ / /计算点u 和v之间的距离 " X% q# S, g' ~8 ~5 H 6 w8 O, p' n; j9 B$ N9 W3 \+ h( T, zfloat dx = u.x-v. x ; * d# P! e+ j0 |( o! g$ v) D* A7 l# c" y+ b7 R7 y- E! X: f' o
float dy = u.y-v. y ; & ]: x% f. P1 ~5 { K5 }1 ]+ \: m) N2 ?$ h( ereturn sqrt(dx * dx + dy * dy); : X: A; L' c7 {0 T. ]3 U u3 u6 M! g$ Y# X: L
}% j6 }) I4 z+ x% |2 C% M& F% Z
# K- l1 L* O. b3 e9 z1 i0 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 ),该函数实际求解最近点对。3 X- `* u A$ c4 ]2 _+ i7 h
8 N5 T' f8 Z! Q/ S' B( q0 y7 a+ q7 I
程序14-10 预处理及调用c l o s e 3 J! }! I* B9 X* w& H+ ^* _+ R5 R, P9 Z$ j5 \; _& \
bool closest(Point1 X[], int n, Point1& a, Point1& b, float& d)+ w4 G# V6 M+ c- ?
) M* }: Z# _- O) x{// 在n >= 2 个点中寻找最近点对8 p9 E- N# t3 W. S& m! R3 z
% M$ d f4 T* V+ U( v// 如果少于2个点,则返回f a l s e 8 W- {! `1 |% [: H- F; ?0 R- o! U8 Z9 E' i& W1 f/ |
// 否则,在a 和b中返回距离最近的两个点* i `/ R# T$ u- V
, j( {: p' x; ^9 o
if (n < 2) return false;$ T% B% r+ E& |9 }5 |
( ~: B2 n0 \; n/ O
// 按x坐标排序 $ o% a( z- l8 F" H: h0 `0 Q p" B2 x: N2 N' P, E! X4 ]3 ]3 m& ~
M e r g e S o r t ( X , n ) ;6 M3 p0 Q+ s6 N( [' }3 U
( m V( y- ~+ n1 L
// 创建一个按y坐标排序的点数组 x; c$ p7 ]. }: K
6 s. W3 B2 U# Y; }
Point2 *Y = new Point2 [n];# o9 i8 q7 J0 [$ X% o; U; D& l
3 z# f& B% P: O6 g. c/ D6 A3 S
for (int i = 0; i < n; i++) {7 M' o: f5 G# q) Q
5 |! `* B8 L" A2 Q& _/ K0 cY.p = i; ' _& J: Z. ^# W2 B! U+ K5 V& { 3 j. l+ P- Q/ T1 I9 ^" aY.x = X.x;. W7 m( k3 F1 n$ `+ Y
9 y- I8 H4 w: e- y9 o; ^9 s
Y.y = X.y; C9 b% v' i+ h 4 {( L& M C: z5 T% w& y" b' Z5 |}( V* Q! v |$ u. X, y
' i' S8 D" q) J. e$ {5 P0 Q2 W
M e r g e S o r t ( Y,n); // 按y坐标排序& J0 N# S/ ?. ^0 ?& S3 A
: H! ~, ^. p* C3 Y// 创建临时数组 2 o/ i' U, m+ k . E% D9 @1 s% B$ `$ lPoint2 *Z = new Point2 [n];- ?9 q) l% d) \6 M( r
2 u9 Z8 ]$ K @2 O; N9 j Q// 寻找最近点对. @8 w( Z! r0 {3 ?: W' J3 Q# K% o
# N, S6 V3 o r* R
c l o s e ( X , Y, Z , 0 , n - 1 , a , b , d ) ; 3 u& z f) r9 z5 p- b2 ]% M; _( [3 Q/ f
// 删除数组并返回 : m) u$ M2 f: c- p$ h & \6 y4 |0 o: w4 edelete [] Y; 3 g C' ^& C7 M/ m/ M7 p$ v 5 r5 \: B+ \& X3 @delete [] Z; : C+ o5 a! S, [/ T ! Y1 g n# l1 Oreturn true; 7 f a9 Y7 h" `' T$ V % H! y. w' l8 Q# | K} d9 l" ^6 P9 o4 H; w3 ]) d- r
: `9 t G B! Z0 b. O程序1 4 - 11 计算最近点对 * K, ]- G% ~, H7 ~3 V' \% O; k0 d4 Q `9 l9 q' Y( X7 H% n4 V
void close(Point1 X[], Point2 Y[], Point2 Z[], int l, int r, Point1& a, Point1& b, float& d) , L; z5 b5 R# T9 L3 m4 w; e6 @* R
{//X[l:r] 按x坐标排序( I6 F" U/ N8 h* T- o
; Z* u% p: a4 t0 f z/ P0 u( q
//Y[l:r] 按y坐标排序6 y G \* Y$ f: m( E; r- g. z9 d
4 U9 ~3 g+ H3 k: R8 j7 G& z0 i
if (r-l == 1) {// 两个点 ( ^/ w4 F3 G7 Q* M K- m9 w1 J% o8 v* F! y1 Y, da = X[l];! _2 D7 }/ p8 `2 {. d" @9 N, p
5 V6 }' R! S# j
b = X[r]; 3 c- |$ X4 T- Y9 Q1 w5 n [) G7 u6 R' z
d = dist(X[l], X[r]);9 O _/ q% Y" Y
1 |$ ]9 ~9 c- k) v8 y5 q1 c( br e t u r n ; } - d, ^/ p+ g0 p. r7 ]' ` J- ]$ y# k% L! T0 w/ d9 I. \
if (r-l == 2) {// 三个点 9 w, w; N9 l) t; U/ q* ]! a2 Q I& K" b# J$ D6 j# |7 J
// 计算所有点对之间的距离( h* N9 k2 Q S' p) l; S
$ x, L; @7 U7 g: S _/ x, \float d1 = dist(X[l], X[l+1]);, ~- t" p8 Z' y
4 v" `. y' `; N$ s2 A, sfloat d2 = dist(X[l+1], X[r]);" D" m4 w; f8 p. X
4 D+ U- A7 S, C! t8 l
float d3 = dist(X[l], X[r]); 5 K2 u1 k- v3 _3 k) B4 \# d. ^ Y3 ^% n8 Y' s: \8 V# F
// 寻找最近点对 x+ ~* |( g4 O: t3 G + _' [: f- h& B- r& K/ Pif (d1 <= d2 && d1 <= d3) {) b* I6 n% c& O8 R, W+ A
8 |' ]9 {2 F6 G& u6 ]4 Q* z& ~9 q3 P
a = X[l]; , C: q; c$ `: s! p ]7 J. U8 v: l3 f' R0 H. Ob = X[l+1];# P9 C, D; w" Q2 E; j+ x
# D* Q4 Z* O* q; cd = d1;. h6 R# ~2 }! u H
! z0 t, q7 y* m! K
r e t u r n ; }. }7 A, e8 g, a/ I' d
m7 r( U: z- j% h
if (d2 <= d3) {a = X[l+1];( \0 W( H$ ?+ L% ]
5 N8 [* I7 C+ H/ r- {9 H+ {* }. P
b = X[r]; b0 E7 w# H0 D8 n. P6 `, z: x! g2 v+ @9 G# g0 {
d = d2;} ( S! M6 [% ]# N) q4 o) O& i& j# X! ]7 e7 l% q
else {a = X[l];9 }: v2 M# @- {) X3 O
! d$ `5 Z# \9 z" F8 F8 j0 O
b = X[r];- Q: `2 n# v2 {! g* g$ `
8 w: s7 `0 _& R4 H( q, @
d = d3;}1 Y& _: Q* N7 v5 o+ v
. C0 M) L, P0 D5 kr e t u r n ; }5 y; s% X/ L8 G
3 z, o- w6 ^( z) o+ z/ \& G
/ /多于三个点,划分为两部分 " f* f1 O5 K+ t/ J. ]2 F ) F6 E7 F3 y! P I& t' w! e( F* [int m = (l+r)/2; // X[l:m] 在A中,余下的在B中( B) l: l! B/ G" j; _' v" M/ x: w% M
% t+ M( @ v% a- T9 I9 T0 l// 在Z[l:m] 和Z [ m + 1 : r ]中创建按y排序的表& p" S2 G7 F; O
, |7 U. w$ t( e) h8 c
int f = l, // Z[l:m]的游标 2 ^( g) }, B* I+ M9 S1 l9 {8 }6 ? d9 y+ W, Q# K* G: O; Q. s1 k
g = m+1; // Z[m+1:r]的游标( O, M; ]8 n" t9 }- }* L3 ^
, ?- y& j; ]4 t' s+ J1 i
for (int i = l; i <= r; i++): F& J% k" U# R- b3 V
+ V0 V+ Q& Z5 ? v# \3 V' l
if (Y.p > m) Z[g++] = Y;0 D2 |, x* T9 P( C# R' n% Y9 Q
) V% O7 G% V; X( V9 @7 \( @else Z[f++] = Y;, ^& p( n4 p; U+ d
% h C; N( R! c$ Z A
// 对以上两个部分进行求解7 _5 m) w! b- U$ v C$ v# F: K( f
0 x- C2 f( F* R: f; K: q$ ]
c l o s e ( X , Z , Y, l , m , a , b , d ) ;2 s0 X- v7 n" G6 O
& G" ~. C9 I4 \4 _& Sfloat dr; 9 L9 m% {' V1 V% p; e$ a6 L1 o5 a+ ?+ l/ \/ v
Point1 ar, br;0 H, I; r4 X3 Q
& W9 T) L' C; J1 F
c l o s e ( X , Z , Y, m + 1 , r, a r, b r, d r ) ;, J& g% k( H/ R
0 |3 d; y: b$ R" D) n// (a,b) 是两者中较近的点对 & o' `- b" D5 ^8 c2 O* ` @! u t1 F' `7 t! _2 ^4 {2 P3 M" P
if (dr < d) {a = ar; 5 @7 x8 N( H! {! L; [7 K, o ' X# w2 w" {* `5 v: G Gb = br; 8 \# U, h$ a! o5 T; L/ C' F# \2 `0 [! q* b
d = dr;}$ e* V0 ]5 {4 B
) v6 \, d: P! v, |' W/ t* a% n: |
M e r g e ( Z , Y,l,m,r);// 重构Y ; C1 w9 F+ @' X0 m u( x( O6 F * n$ E9 h" z6 z# i; G' T5 ^0 o/ /距离小于d的点放入Z : a% o& M. ^9 C# l 3 D g! W6 x9 Y1 pint k = l; // Z的游标) x( U7 I; b0 z$ c" j
2 M" g1 j: D* Hfor (i = l; i <= r; i++) / p+ c7 E1 o1 I1 J7 h: S. Z2 i& A8 l- P+ @
if (fabs(Y[m].x - Y.x) < d) Z[k++] = Y; G$ Q0 C. L1 @, Z7 {. }9 B7 V9 V3 a$ h. }2 y
// 通过检查Z [ l : k - 1 ]中的所有点对,寻找较近的点对9 g+ d4 ]* Z" N$ O2 f
; c* D% r8 f7 ifor (i = l; i < k; i++){1 J, u% s/ w- T
1 G; o& W b5 R# _- i+ K h: M# h
for (int j = i+1; j < k && Z[j].y - Z.y < d; " @% G& H1 z, {! q / E1 b1 @. v0 ]% J: N0 Nj + + ) {/ A6 B2 {5 }) y. A/ @$ f/ _/ c% }
" \8 ~5 N/ C: c6 V# ^1 `6 f9 f
float dp = dist(Z, Z[j]); 2 ^. N+ e) V" ?5 y- I3 `, O$ F' ^. q7 J2 }5 j3 F
if (dp < d) {// 较近的点对) H1 R w( F2 U% V, S9 y8 g0 `
8 U5 b: h o4 K2 G9 `) E! T9 e
d = dp; % k. ^4 X. s5 i+ D2 ~+ G7 q" J+ V" R! i
a = X[Z.p];; M2 [4 V: l6 `7 y0 S
) @* Z' f/ J' X+ [! Z+ a
b = X[Z[j].p];}8 `- n, A0 }9 V' M7 `# [7 h) X
) N7 e1 i9 A) M" D
}3 G8 J" n# O) {5 c& s4 D
5 m* u8 e8 k4 y. X: w. P- M4 d
}9 Y% ~8 R$ N0 `) V3 a9 P! V; E9 p
, b' q0 N' @, o) Q
}; A) F0 m) v0 B/ f2 S; I2 W
2 L+ L+ S7 E0 y' V
函数c l o s e(见程序1 4 - 11)用来确定X[1:r] 中的最近点对。假定这些点按x 坐标排序。在Y [ 1 : r ]中对这些点按y 坐标排序。Z[ 1 : r ]用来存放中间结果。找到最近点对以后,将在a, b中返回最近点对,在d 中返回距离,数组Y被恢复为输入状态。函数并未修改数组X。 . V# t6 f0 f8 O' J: O ! D* m, L) A! d& d5 H0 c. d9 p首先考察“小问题”,即少于四个点的点集。因为分割过程不会产生少于两点的数组,因此只需要处理两点和三点的情形。对于这两种情形,可以尝试所有的可能性。当点数超过三个时,通过计算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 ]。9 I6 [& I6 M5 k% _# V
i4 a0 J; a. 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,不管该点是在RA7 O5 `* l( ]! O8 {
, A; Y) q3 P4 B, E6 e, n. ]还是在RB中)与Z[j] 配对来实现,其中i<j 且Z [ j ] . y - Z [ i ] . y< 。对每一个Z [ i ],在2 × 区域内所检查的点如图1 4 - 1 7所示。由于在每个2 × 子区域内的点至少相距。因此每一个子区域中的点数不会超过四个,所以与Z [ i ]配对的点Z [ j ]最多有七个。 * b+ R. v, B9 [2 r1 u, h/ H# |1 X" T
2. 复杂性分析 7 H2 f% ]9 m! b 4 W4 G& F5 U1 x0 y3 D8 M令t (n) 代表处理n 个点时,函数close 所需要的时间。当n<4时,t (n) 等于某个常数d。当n≥4时,需花费(n) 时间来完成以下工作:将点集划分为两个部分,两次递归调用后重构Y,淘汰距分割线很远的点,寻找更好的第三类点对。两次递归调用需分别耗时t (「n /2ù」和t (?n /2?).7 w" U, I3 {$ X1 U% o
7 S3 [$ e1 I3 d) q9 Q, w
这个递归式与归并排序的递归式完全一样,其结果为t (n) = (nl o gn)。另外,函数c l o s e s t还需耗时(nl o gn)来完成如下额外工作:对X进行排序,创建Y和Z,对Y进行排序。因此分而治之最近点对求解算法的时间复杂性为(nl o gn)。</P>