问题描述:
给定平面上的n个点,找出其中的一对点,使得在n个点组成的所有点对中,该点对的距离最小。
算法思路:
看见这个问题,我们第一时间能想出的最笨的方法就是:将每一个点与其他n-1个点的距离算出,找出最小距离,可以见得复杂度很高的,那还有什么办法呢?
一维最接近点问题:
为了使问题易于理解和分析,先来考虑一维的情形。此时,
S
中的
n
个点退化为
x
轴上的
n
个实数
x1,x2,…,xn
。最接近点对即为这
n
个实数中相差最小的
2
个实数。
假设我们用
x
轴上某个点
m
将
S
划分为
2
个子集
S1
和
S2
,
基于
平衡子问题
的思想,用S中各点坐标的中位数来作分割点。
递归地在
S1
和
S2
上找出其最接近点对
{p1,p2}
和
{q1,q2}
,并设
d=min{|p1-p2|,|q1-q2|}
,
S
中的最接近点对或者是
{p1,p2}
,或者是
{q1,q2}
,或者是某个
{p3,q3}
,其中
p3∈S1
且
q3∈S2
。
如果
S
的最接近点对是
{p3,q3}
,即
|p3-q3|<d
,则
p3
和
q3
两者与
m
的距离不超过
d
,
即
p3∈(m-d,m]
,
q3∈(m,m+d]
。
由于在
S1
中,每个长度为
d
的半闭区间至多包含一个点(否则必有两点距离小于
d
),并且
m
是
S1
和
S2
的分割点,因此
(m-d,m]
中至多包含
S
中的一个点。由图可以看出,如果
(m-d,m]
中有
S
中的点,则此点就是
S1
中最大点。
因此,我们用线性时间就能找到区间
(m-d,m]
和
(m,m+d]
中所有点,即
p3
和
q3
。从而我们用线性时间就可以将
S1
的解和
S2
的解合并成为
S
的解。
![](https://img-blog.csdnimg.cn/ad426c15655944b48ec3310b9273036f.png)
二维最接近点问题:
下面来考虑二维的情形。
选取一垂直线
l:x=m
来作为分割直线。其中
m
为
S
中各点
x
坐标的中位数。由此将
S
分割为
S1
和
S2
。
递归地在
S1
和
S2
上找出其最小距离
d1
和
d2
,并设
d=min{d1,d2}
,
S
中的最接近点对或者是
d
,或者是某个
{p,q}
,其中
p∈P1
且
q∈P2
。
考虑P1
中任意一点
p
,它若与
P2
中的点
q
构成最接近点对的候选者,则必有
distance(p
,
q)
<
d
。满足这个条件的
P2
中的点一定落在一个
d×2d
的矩形
R中。由d的意义可知,
P2
中任何
2
个
S
中的点的距离都不小于
d
。由此可以推出矩形
R
中最多只有
6
个
S中的点。
证明:将矩形R的长为2d的边3等分,将它的长为d的边2等分,由此导出6个(d/2)×(2d/3)的矩形。若矩形R中有多于6个S中的点,则由鸽舍原理易知至少有一个(d/2)×(2d/3)的小矩形中有2个以上S中的点。设u,v是位于同一小矩形中的2个点,则
distance(u,v)<d。这与d的意义相矛盾
因此,在分治法的合并步骤中最多只需要检查6×n/2=3n个候选者
为了
确切地知道要检查哪6个点
,可以将
p
和
P2
中所有
S2
的点投影到垂直线
l
上。由于能与
p
点一起构成最接近点对候选者的
S2
中点一定在矩形
R
中,所以它们在直线
l
上的投影点距
p
在
l
上投影点的距离小于
d
。由上面的分析可知,这种投影点最多只有
6
个。
因此,若将
P1
和
P2
中所有
S
中点按其
y
坐标排好序,则对
P1
中所有点,对排好序的点列作一次扫描,就可以找出所有最接近点对的候选者。对
P1
中每一点最多只要检查
P2
中排好序的相继
6
个点。
复杂度分析:
T(n)=O(nlogn)
实现代码:
int Partition(point P[], int first, int end){
//初始化待划分区间
int i = first, j = end;
while (i<j)
{
//右侧扫描
while (i<j&&P[i].y <= P[j].y)
{
j--;
}
//将较小的记录换到前面
if (i<j)
{
int temp = P[i].y;
P[i].y = P[j].y;
P[j].y = temp;
i++;
}
while (i<j&&P[i].y <= P[j].y)
{
i++;
}
if (i<j)
{
int temp = P[i].y;
P[i].y = P[j].y;
P[j].y = temp;
j--;
}
}
return i;
}