1 i. p) g3 s: S0 C6 i0 W" |+ Z1 c$ p+ ?: `) q8 _+ P
if (n较小) {用直接法寻找最近点对% n& U7 E8 y/ @# ]2 ~! [
( j2 d6 \' q U w: pR e t u r n ; } / N" K, _+ C9 Y& j8 e6 M2 ?3 H9 t/ Z: T2 j4 m1 T0 o
// n较大 - O: H: N# n' g1 k0 }" C - t i0 U4 I+ s4 w将点集分成大致相等的两个部分A和B + N2 H+ s6 o7 ~. K/ @- N3 E# n x; o4 i
确定A和B中的最近点对. c2 N: L, {1 c2 m
: m9 k8 I* L: I' ]3 Q: K; C
确定一点在A中、另一点在B中的最近点对 & z# g. M$ Z) F$ j0 [+ j ' T6 B( T' d0 R& e; }, Z从上面得到的三对点中,找出距离最小的一对点 ) d* i( O- _( C! l/ G3 B 0 G4 V- P4 L+ x' B图14-13 寻找最近的点对 $ w) E( D2 O1 K7 D' y8 Z1 M0 B5 ]; d( N0 R* C3 C. {2 U+ p
% N$ D% g: N5 b# D# S8 L
为了确定第三种情况下的最近点对,需要采用一种不同的方法。这种方法取决于点集是如何被划分成A、B的。一个合理的划分方法是从xi(中间值)处划一条垂线,线左边的点属于A,线右边的点属于B。位于垂线上的点可在A和B之间分配,以便满足A、B的大小。 / d( K, u4 Z, O) g+ }3 V9 H, O. Y* t: S& |- p5 b9 o
例2-8 考察图14-14a 中从a到n的1 4个点。这些点标绘在图14-14b 中。中点xi = 1,垂线x = 1如图14-14b 中的虚线所示。虚线左边的点(如b, c, h, n, i)属于A,右边的点(如a, e, f, j, k, l) 属于B。d, g, m 落在垂线上,可将其中两个加入A, 另一个加入B,以便A、B中包含相同的点数。假设d ,m加入A,g加入B。 , p# }4 a( P \: r6 }4 [- f& v% }$ v6 D" s; c# n9 _3 d
设是i 的最近点对和B的最近点对中距离较小的一对点。若第三种情况下的最近点对比小。则每一个点距垂线的距离必小于,这样,就可以淘汰那些距垂线距离≥ 的点。图1 4 - 1 5中的虚线是分割线。阴影部分以分割线为中线,宽为2 。边界线及其以外的点均被淘汰掉,只有阴影中的点被保留下来,以便确定是否存在第三类点对(对应于第三种情况)其距离小于。用RA、RB 分别表示A和B中剩下的点。如果存在点对(p,q),p?A, q?B且p, q 的距离小于,则p?RA,q?RB。可以通过每次检查RA 中一个点来寻找这样的点对。假设考察RA 中的p 点,p的y 坐标为p.y,那么只需检查RB 中满足p.y- <q.y<p.y+ 的q 点,看是否存在与p 间距小于的点。在图14-16a 中给出了包含这种q 点的RB 的范围。因此,只需将RB 中位于×2 阴影内的点逐个与p 配对,以判断p 是否是距离小于的第三类点。这个×2 区域被称为是p 的比较区(comparing region)。 ) Y8 A; j6 j3 ]: I9 v m 4 b% q- }4 j2 L例2-9 考察例2 - 8中的1 4个点。A中的最近点对为(b,h),其距离约为0 . 3 1 6。B中最近点对为(f, j),其距离为0 . 3,因此= 0 . 3。当考察是否存在第三类点时,除d, g, i, l, m 以外的点均被淘汰,因为它们距分割线x= 1的距离≥ 。RA ={d, i, m},RB= {g, l},由于d 和m 的比较区中没有点,只需考察i即可。i 的比较区中仅含点l。计算i 和l的距离,发现它小于,因此(i, l) 是最近的点对。- U: H' q# T1 P6 E) k- z
& Z4 z6 D* f9 d9 {
为了确定一个距离更小的第三类点,RA 中的每个点最多只需和RB 中的6个点比较,如图1 4 - 1 6所示。7 W3 R. ~8 I2 K4 b- N. `
5 Q8 n1 ~! Q, n% `! w1. 选择数据结构 $ J7 F2 Q/ ~* R8 L P {+ t9 h6 ^6 n. a
为了实现图1 4 - 1 3的分而治之算法,需要确定什么是“小问题”以及如何表示点。由于集合中少于两点时不存在最近点对,因此必须保证分解过程不会产生少于两点的点集。如果将少于四点的点集做为“小问题”,就可以避免产生少于两点的点集。7 {; }0 @* h" h a1 p
4 E! Z5 W. W3 @+ f- O9 |每个点可有三个参数:标号, x 坐标,y 坐标。假设标号为整数,每个点可用P o i n t l类(见程序1 4 - 8)来表示。为了便于按x 坐标对各个点排序,可重载操作符<=。归并排序程序如1 4 -3所示。1 y' H9 \6 X& D+ D( E, U5 {
7 s: e1 @; l/ w8 @0 }" O. j% l
程序14-8 点类' p/ `0 U" [/ C
( ~/ o6 ? z v% R, _7 ?$ U% oclass Point1 {) p @8 y+ |2 L: C" W
% g2 Y B) H+ k5 `friend bool closest(Point1 *, int, Point1&, Point1&,float&);4 S2 F" z6 ~" p
- F( d/ Q- I# {+ R
friend void main();! F5 v0 R, Y2 I) ]# n! Q# f
/ f- Y3 [0 }. N/ X1 Pp u b l i c :* M$ O" g! Q7 _9 v
/ `: `: N B& V! T* @5 n
int operator<=(Point1 a) const " e% J# N/ P B7 S8 |- h# E5 v8 k5 c* s- x
{return (x <= a.x);}' y+ \( V5 M, U5 T3 O
% `; M2 `$ a( W! `p r i v a t e : 9 @4 Q* }8 N4 {3 w 6 V0 S4 F$ N \6 h1 ?int ID; // 点的编号 % a7 s0 y# b6 u. B6 y! t) t) M/ m d4 S, z7 S8 P, C! g2 W: Y
float x, y; // 点坐标 9 U2 x7 L6 U; |9 s" c# N: o' k$ A* {2 I1 Q
} ;" J8 W9 L) C2 w
% O4 [$ X: H1 S' V: s# D( I
class Point2 {' F! z" P$ `) G5 B) T2 p q* A; M. [
# E0 N' m7 r3 N( f" s* ^# \1 C* S} ; : U# o+ w# d# P 4 Q4 M) |5 Z- u! 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中的对应点。8 M9 U8 |4 {2 i3 K$ q: m: d
9 q: K& c- ~+ D8 F3 ?
确定了必要的数据结构之后,再来看看所要产生的代码。首先定义一个模板函数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类的友元。8 ^+ ?& w2 s# i8 c; ^* s0 t
9 ]3 M6 x% c# R9 T, w2 y程序14-9 计算两点距离 ; m3 R1 D: g1 Z6 ]0 t( B l8 ?7 a) g. K7 K
template<CLASS T>% Y; {7 Z' ?. V2 d5 Y! ?5 }7 E
9 |) k* ~! a# i+ C
inline float dist(const T& u, const T& v) ; G" G, I9 f; ?8 k5 x9 M$ O 4 z+ A4 F7 d$ `5 ?( p{ / /计算点u 和v之间的距离: F+ g" @+ p9 k: \2 P1 s
3 L$ P# v/ W' A7 D+ d/ N
float dx = u.x-v. x ; 9 m8 I+ x9 `. `2 J% n3 C& @* c7 I" {- _. ~. f( K: O- h
float dy = u.y-v. y ; 2 ?7 P) J4 A: a% N+ v - [, m: w4 |6 p" g6 Q+ Xreturn sqrt(dx * dx + dy * dy);5 |* G! ~$ ]2 g
5 {( D, b5 s, \. K& {0 b
} : j& q6 S$ r2 G5 o9 _8 B; Z( D - a6 T z v2 V! ?如果点的数目少于两个,则函数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 Q; o8 L/ N3 {) ]" j6 @ + z3 J) x9 z/ B3 o% K4 R! E7 K程序14-10 预处理及调用c l o s e, R7 A8 w3 c, ~; i
, W! }" r& b$ H& |bool closest(Point1 X[], int n, Point1& a, Point1& b, float& d) / Z1 y5 p. I+ H7 z% P g, Z , F/ y% |! p5 H$ ?& M{// 在n >= 2 个点中寻找最近点对 9 q8 w. S, X2 v; u1 P" B: |, P8 c! A
// 如果少于2个点,则返回f a l s e A, b+ ~3 f0 w# P8 c0 O8 R9 l, q m+ i
// 否则,在a 和b中返回距离最近的两个点 # J0 c& g) m; V: \% u . h" H$ E1 T3 {6 vif (n < 2) return false; ! E7 u6 [/ a5 A& y$ [6 L6 D* \- X! Z' |% ]1 C; u* ?; ~
// 按x坐标排序 8 ?5 S2 K; a, E' Q: W+ |7 y8 k" `* v/ b4 ?% Q) T) U! N
M e r g e S o r t ( X , n ) ;, s% x* h) o6 @! ~# |4 J
' s! \! q" X% U8 W
// 创建一个按y坐标排序的点数组. z( Q6 M5 k" U) L4 R' L
# a& O6 V9 s, f0 X4 M9 i# c+ QPoint2 *Y = new Point2 [n]; 5 t6 X Y! `. A% V% l- J1 v+ ~0 ?. f! b# _) v
for (int i = 0; i < n; i++) {# S. T+ ]5 N3 Q: t' d) N
8 v- I/ w. R8 [7 D" a// 将点i 从X 复制到Y ( c7 I3 o' H+ H2 n ( c2 [" W: G3 Q: O8 ]. KY.p = i;9 B" V) S* M4 F4 \, @& ^: E
5 l2 P$ X! s- l+ A
Y.x = X.x;3 Y3 k/ E3 H; \9 R1 x, ]: s
) |2 \) V* `. j: w+ d! P
Y.y = X.y; - ~9 c3 l% _' P+ J" R6 @- y9 I* H3 p5 C* ?
}/ F9 h, b- G3 t+ ^( `$ Z" z+ R
% a+ u N- e5 J
M e r g e S o r t ( Y,n); // 按y坐标排序 , y3 u- X5 p/ w/ R ( Z3 t; d" |4 u" e w// 创建临时数组 2 E1 y7 a7 |* T7 A 5 [& W0 _6 I( W' D( P# |Point2 *Z = new Point2 [n];& H" \* m9 P$ @/ D7 n
- A, j( c- a) Q/ \
// 寻找最近点对! F% h, G% }% n6 ~, c% x3 ]
( z( Y* b! N# c4 ?- Nc l o s e ( X , Y, Z , 0 , n - 1 , a , b , d ) ; d, L1 ^! B' }$ a % `1 j# j, \6 N* {) O! L// 删除数组并返回' G3 ?9 U( [0 _& E: @
4 x9 ?; E' A0 y4 h. ?9 e{//X[l:r] 按x坐标排序 ! X) h% H' B6 w. d! ?! `. Z" E% @3 a
//Y[l:r] 按y坐标排序. Z# z" ], ]9 G4 g6 ]2 ]: O
2 T5 K# ]# n5 Lif (r-l == 1) {// 两个点0 D1 O3 B' z8 Y) a4 d( S
4 ~7 J( v" u5 ~8 `
a = X[l];( x$ d1 Z( V& ]: ?7 P
& } f8 n7 U- ?# C# o
b = X[r];$ Q0 X! ~0 x2 p! I9 d8 i
) o" S% S& P% t# H. I" H- Y
d = dist(X[l], X[r]); , J, b. o- P% [; @4 J# I ( h. s' I) t* |4 r+ X2 Mr e t u r n ; } ( X/ m- z3 B, p/ | 9 v" ~8 j' q2 l0 r: rif (r-l == 2) {// 三个点; c6 m# H# h" t/ `- Z( S
* Z7 X# Z- ^, V
// 计算所有点对之间的距离) L4 u! E$ S9 F" z3 A1 N
% x5 E( Y0 p2 V- z$ ?8 |
float d1 = dist(X[l], X[l+1]);' I* b* R0 O9 z" _3 l* J
# M" w# ^1 E, O) P6 \
float d2 = dist(X[l+1], X[r]);" { q* |. }# s i
8 O$ I$ J5 U8 h$ h+ h6 }+ v6 ]
float d3 = dist(X[l], X[r]);# X' h+ J* Z2 q. a8 X& b
4 u; b! A4 B. [: g* l4 p
// 寻找最近点对' y, B; R7 M# ?
: o0 l& [6 a% Y. T6 ^) h8 G
if (d1 <= d2 && d1 <= d3) { , R `! A( l/ K9 |: }' s* i . B" h. ~- d1 @: Q3 [# Wa = X[l];, B( Q" I# \0 ~2 T0 q
$ K7 J9 \% L( f: T7 z' H' w+ ub = X[l+1]; 9 C5 s) c4 s1 \, ^0 V) E, k2 c( N# B& ^/ m* \1 z$ `
d = d1; 1 l5 U- g8 K9 g) I1 ? * h% P$ g. H; E* Q5 b' fr e t u r n ; } ! w# \$ Q( o7 ? / R# L, S3 c' R! Q# s; ]3 j+ x8 Yif (d2 <= d3) {a = X[l+1];- J7 P: L" E9 b4 i
, H0 i% X, @9 z& d& D
b = X[r];7 V2 W+ s: Q" ]2 I
( |% }1 j4 l7 J4 }9 j; Fd = d2;} , U) u3 H( h# x1 y; G! ]" N, ^' [; d
else {a = X[l]; ! ^' c) M- P- J7 \; {' g9 Y- i! J$ m3 I# B
b = X[r]; : r- o$ c2 x& I3 T# t3 ?5 m$ E7 f x8 \; c+ U
d = d3;}0 E& C5 r8 j; z; b7 x2 D5 M& q
4 h, X4 T* |0 Z3 y/ k! Nr e t u r n ; } 8 g2 h, }, P% b# g+ l E & O" E$ ]: t0 D8 }" ], x/ /多于三个点,划分为两部分 6 k- J M( v& C, g : \! i! q5 m. H. N- T2 t. oint m = (l+r)/2; // X[l:m] 在A中,余下的在B中 ; f' Q% S' D0 K7 w/ t1 R' T: v" B, k: n+ F
// 在Z[l:m] 和Z [ m + 1 : r ]中创建按y排序的表 4 h7 \/ X, i; L1 }8 b* ^ : U) w/ z; P5 I6 iint f = l, // Z[l:m]的游标: l+ D. X/ P, x/ ^
! ?0 ~' |8 D, b5 V2 {3 }5 m
g = m+1; // Z[m+1:r]的游标 4 [) m8 S6 K ~' i& o- R. W- k- e& ]6 o' n. U
for (int i = l; i <= r; i++) - Z T; Z1 h8 O! s, ] 2 a8 f+ G1 A, ~$ B- o# Q/ M0 E& Iif (Y.p > m) Z[g++] = Y;) I- w- {0 _2 |) ~% u% `
9 L: E+ d) c* c0 t4 @# _else Z[f++] = Y;! e% L5 {. a( e5 S( I
& e) @& Z7 s& \2 f3 ]
// 对以上两个部分进行求解8 w" ?9 H, N% Z* h2 }, p
4 I$ d% O7 C+ `. f( L! t) p) A) }
c l o s e ( X , Z , Y, l , m , a , b , d ) ;: V/ O- a) c# j
& V& R) \- b( z- H B9 j- g" Yfloat dr; # e" M% _0 d2 D& b6 K1 `6 o6 O7 H
Point1 ar, br; 0 X: W9 @5 o, w 6 z% b6 C o2 w3 A2 w. gc l o s e ( X , Z , Y, m + 1 , r, a r, b r, d r ) ;% D8 @6 h) ~4 k5 f& P# t
$ z1 r3 k# k& G2 @9 U
// (a,b) 是两者中较近的点对 0 ?$ P+ h8 w, L7 B& V" n: U( F8 S% } ) o0 F6 W- o" O8 Y, o+ n1 G. Xif (dr < d) {a = ar; " p* |8 s+ m9 K' m 3 \, x! |3 N" J1 {b = br; 6 u" [( g6 Y& L$ T0 {: U' F6 Y( B7 A' }4 A
d = dr;}! Q5 F$ b0 e# q
. M( |' Q+ _* C! w V
M e r g e ( Z , Y,l,m,r);// 重构Y 9 @6 m7 v$ |4 A7 G4 {( ^: G5 Q * b/ @2 m6 d2 T0 M! Z/ /距离小于d的点放入Z t" O5 s6 @( ~- E* R/ h5 f
' W! O$ G3 N7 l) b: h; `$ Yint k = l; // Z的游标 : {, U9 Q! K8 L1 D+ I: H" X: z+ ~9 J5 s J
for (i = l; i <= r; i++)+ S0 l) |5 y. y4 x% c/ h
% l9 C) u0 [" i( d# M
if (fabs(Y[m].x - Y.x) < d) Z[k++] = Y;1 s/ i+ Q8 Y3 d0 L6 h) P0 [) @$ @
$ D2 P U* a/ g! n/ x: |// 通过检查Z [ l : k - 1 ]中的所有点对,寻找较近的点对7 Y: s3 ^& q/ g
R$ s, ?, R8 A* G这个递归式与归并排序的递归式完全一样,其结果为t (n) = (nl o gn)。另外,函数c l o s e s t还需耗时(nl o gn)来完成如下额外工作:对X进行排序,创建Y和Z,对Y进行排序。因此分而治之最近点对求解算法的时间复杂性为(nl o gn)。</P>