, Q8 h* O& @. p& V5 X- r # Z% ^7 z4 s/ \if (n较小) {用直接法寻找最近点对 & B! y D8 z6 w9 u a% b# G+ |# a 8 b. y4 x/ x% l) e3 iR e t u r n ; } 9 H! b$ o7 q8 p! m2 J+ g9 I2 {* T4 P9 P
// n较大 4 f) W. L( s. }% ?5 s ; Z8 X' a6 P' \. A) W& d6 ^将点集分成大致相等的两个部分A和B4 U9 w7 M1 ?2 M
* f& {7 Z' V5 o* T/ u& `例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) 是最近的点对。8 n' |) D5 b0 H" Q5 z
! L! v2 s' ~5 Y/ c1 A为了确定一个距离更小的第三类点,RA 中的每个点最多只需和RB 中的6个点比较,如图1 4 - 1 6所示。 3 [3 @$ y8 J7 X- N - u# O3 _: |" U2 X1. 选择数据结构/ y- X7 z @2 [8 L" L
2 {# [" `+ W `4 t! _' |为了实现图1 4 - 1 3的分而治之算法,需要确定什么是“小问题”以及如何表示点。由于集合中少于两点时不存在最近点对,因此必须保证分解过程不会产生少于两点的点集。如果将少于四点的点集做为“小问题”,就可以避免产生少于两点的点集。 % l; e: V9 W5 T, G+ ~! g, O$ f" ~1 `/ K2 P4 u# Q0 f
每个点可有三个参数:标号, x 坐标,y 坐标。假设标号为整数,每个点可用P o i n t l类(见程序1 4 - 8)来表示。为了便于按x 坐标对各个点排序,可重载操作符<=。归并排序程序如1 4 -3所示。4 E/ g- R, \1 B0 d; h- d
0 h/ [0 ^1 N7 @" }3 Z程序14-8 点类 + i2 A* T& T' o1 b# ^ . n! R* [" Y( R7 M4 wclass Point1 {1 l. Y) _, j- O y3 T! |' R6 L. U
( U1 @' R l' x0 X- l6 t
friend float dist(const Point1&, const Point1&); ( o/ t, F. O) {/ h8 i, L8 P! W9 C6 |) _7 }1 j- o' V
friend void close(Point1 *, Point2 *, Point2 *, int, int, Point1&, Point1&, float&);4 ?" a0 F7 j0 V5 h7 L, ]
1 L# k" u# R: Q+ ~! L9 l( i
friend bool closest(Point1 *, int, Point1&, Point1&,float&); 9 _9 M( v, j$ F6 f; N 7 d( `# B! E8 m) S1 |+ X' G' ?friend void main(); # Q. M6 p- m) ^- h / j! K+ L& j1 C4 Kp u b l i c :& i2 ~" i+ O/ e2 Z8 m+ E
/ ^9 o: c: |( `& J# Tint operator<=(Point1 a) const4 [: ^6 D* I i5 }% x
# ~4 {8 W, d. b, \+ s! X{return (x <= a.x);}4 {6 c: |% ~7 v4 `) P
E, F9 D, T: s# V: E( s" c
p r i v a t e : . G- N1 b# A. Y4 Y1 L, P$ k, u" C1 A- ]% b$ f, p
int ID; // 点的编号0 `$ \: |+ n# I4 c5 a
* t. c$ q, C; k6 h2 G* \1 Q
float x, y; // 点坐标3 F6 d2 I, I% m- X
! J& e2 p- T6 G7 }4 M' Q) xclass Point2 { M/ ]( Q; r. _5 b . F: o2 ^: y @ Z! q3 T& l0 Nfriend float dist(const Point2&, const Point2&);3 b# N* F8 c. ` u+ T% J/ [& C3 s
' E, Y5 o: t( `3 R l
friend void close(Point1 *, Point2 *, Point2 *, int, int, Point1&, Point1&, float&); 3 J1 [0 S7 ^7 A4 S! C9 U # {# m6 M8 k1 k& n6 Sfriend bool closest(Point1 *, int, Point1&, Point1&, float&);5 ^- F& o1 p/ @5 B1 Q6 Z
) w7 l' V) T# j3 d6 Y
friend void main();$ \# d4 G& D: x# K
! I2 ]5 F" f2 F7 E
p u b l i c :, b1 i% h7 ~6 F5 q5 H5 d
. k: v+ c* ~: n8 g* V
int operator<=(Point2 a) const6 m/ o: [4 ^; c7 g7 g0 |7 j
8 J2 f4 O7 C9 F7 ` S8 }7 R{return (y <= a.y);} - J: a+ c$ g7 t: V' Y ) i! h6 L) a E5 x& Y: X! ~; xp r i v a t e : ) v- y% G; [+ a; T, {* |& l- m$ U5 r$ d* U
int p; // 数组X中相同点的索引 ; X8 I( e7 Q9 P* N2 X. f0 K 9 w+ s9 a8 N' Pfloat x, y; // 点坐标 $ Z1 O' _: K9 q% Y- W$ E: Z9 K* X 5 K) a- q( K- P/ o' Q4 X$ s9 V} ;3 t( o {4 f* m q. y4 J# X
+ r$ O! @4 b2 b# Y. i所输入的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中的对应点。 o- K, L* W% Q8 j0 b: ]
+ g, h: ]' `1 s) u确定了必要的数据结构之后,再来看看所要产生的代码。首先定义一个模板函数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类的友元。; @! X' c( `6 A m
8 b# N/ z8 t* J
程序14-9 计算两点距离0 j; B$ T( g/ e
" A8 i$ \9 |4 z G' Y1 rtemplate<CLASS T> ( y& p5 e- f0 H( x$ c0 q+ j' T; s; n
inline float dist(const T& u, const T& v), F' q" O, l1 S! J
4 N& s! b8 W% ?' s# i) Z
{ / /计算点u 和v之间的距离 0 |( W; e0 F" ~& G3 ~ ) [6 ]! F3 x8 H1 h3 _# K0 ?( ]! W/ ]float dx = u.x-v. x ;! L0 i- t1 M2 Y6 M. u
2 Z+ Q& ]6 |* O! F: h/ \: F
float dy = u.y-v. y ; & ?$ w% Q6 } t7 [% }' c6 [ 5 q' q5 n3 A) \: @/ o) z8 V1 T, l1 T! Qreturn sqrt(dx * dx + dy * dy);3 n+ g* f4 w1 q; B, o
1 a6 D# L# C$ H# i. Y
} ' x" r S3 Q- p/ u4 z0 N9 B3 x / m7 j4 @$ s, [9 Y% C5 t如果点的数目少于两个,则函数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 ),该函数实际求解最近点对。 ( G+ X+ c& p. x6 J 3 k2 K1 J# ]+ {0 ?/ u: s& ~; @6 y# d ^程序14-10 预处理及调用c l o s e p, C" ]+ K8 i: _1 p) O: x$ O2 E; J1 S9 \
bool closest(Point1 X[], int n, Point1& a, Point1& b, float& d) $ p1 i' @6 H% x. v+ G" T4 R ! u1 k. H, w. g8 i4 O2 o7 v' h{// 在n >= 2 个点中寻找最近点对 % y3 D& L; f8 f4 F6 a9 | ! q. H+ W. R; y. {// 如果少于2个点,则返回f a l s e 1 J) ^% u$ N& U$ l! v: U3 r' c, s: z: X) A4 }8 F1 l
// 否则,在a 和b中返回距离最近的两个点4 d' s4 P: C: G$ }& w' g
+ x9 t1 `: t8 c8 Cif (n < 2) return false; 0 A! S/ P, |- c- v0 W / J. R. \# V }# W3 ~// 按x坐标排序 * M7 w/ T' m- E # K1 d8 b$ w1 |; P5 ]5 PM e r g e S o r t ( X , n ) ; \0 L; ^$ f. L
" j6 k" Q0 s" t0 {" J5 a9 C, P// 创建一个按y坐标排序的点数组" m8 Y& n. J d: k5 l& k
7 s& |! S9 i+ t# k
Point2 *Y = new Point2 [n];& D* P( t, H$ M( M8 j; Y- J# @
% L( z* z/ I3 S, l& B( ]
for (int i = 0; i < n; i++) {1 W- ]" s+ j8 G' S5 {8 X( t$ x
9 ?$ q \1 A1 D' t4 r* t// 将点i 从X 复制到Y ! Z& }/ ^* v" e- _- J: E- m- b# a$ v( }* c( p
Y.p = i; % i# G9 @! {" C ^+ c4 q, H: V% O- K$ f% e! y
Y.x = X.x; 9 ~2 M7 K/ T; k3 T1 B( A: h( ]4 E: Y* U7 l+ N Z4 d6 h0 F& b
Y.y = X.y; % [2 X1 U- A! S8 K( q( h 0 b- \3 K U* X5 `1 l3 j; K} ; m9 u3 c, ]6 W/ F , K4 Q0 u! C) r' r+ _# E. O1 gM e r g e S o r t ( Y,n); // 按y坐标排序9 s& C# a: M" F- `
1 v- ?$ o s! J8 ^3 O
// 创建临时数组. |& a7 { |. E
% X2 N4 B% ` z9 U3 K& I
Point2 *Z = new Point2 [n];" ^* g- ?1 S; ]$ r
& r/ o' z. E% Z* Q; S. P
// 寻找最近点对 3 C+ ]7 w: E$ {0 ^& e& O# `3 N( J* ~, U# O8 O' Z2 F) N
c l o s e ( X , Y, Z , 0 , n - 1 , a , b , d ) ;8 S0 p! l$ R4 {( G! \
% `6 D8 S. n: S! P// 删除数组并返回2 e: F6 p! t# d' J, ~! D2 [0 e( h
0 {& I) q7 i+ u3 y
delete [] Y; ) F) d/ D0 [; g! c; g' {# I, o- u9 v. a9 I
delete [] Z;( J. t* W0 p" Q4 w% a. _* x
* }- R, t$ L1 K/ Y这个递归式与归并排序的递归式完全一样,其结果为t (n) = (nl o gn)。另外,函数c l o s e s t还需耗时(nl o gn)来完成如下额外工作:对X进行排序,创建Y和Z,对Y进行排序。因此分而治之最近点对求解算法的时间复杂性为(nl o gn)。</P>