问题:给定n个点的二维坐标,找出距离最短的两点
步骤
首先需要对所有点进行排队,在这里我们需要分别对x坐标和y坐标进行排序,最后得到两个排序数组
第一步,将所有点分为两部分
在这里我们以第n/2个点为界将所有的点分为两部分,分别对左右两个部分求最邻近点对,之后我们会得到d1和d2,然后我们令d=min(d1,d2)
此时我们已经求出来左右两个部分的最邻近点对,但是有可能存在两个点分别属于两个部分,并且他们的距离<d,
第二步
在此我们取第n/2个点的x坐标,比较所有在【x-d,x+d】内点的距离,但是如果一个一个去比较,复杂度仍旧是O(n^2),于是采用如下方法
取【x-d,x】内的一点,将该点与x属于【x,x+d】,【y-d,y+d】内的点进行比较(此处的y是指左侧所取的点的纵坐标)
此时右侧所取到的点的个数最多为6个,因为若有第七个,那么在有部分一定存在距离比d小的邻近点对,与之前求出的d2矛盾。
这样复杂度就简化到O(n)
第三步
将在【x-d,x+d】内求得的最短距离与d进行比较,这样就可得出所有点的最邻近点对。
伪代码如下
代码如下
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#define MAX 100
int a,b;//记录点的位置
struct point{ //坐标点结构体
int x,y;
}z[MAX];
double distance(point z1,point z2){//求两点间距离
double t=(z1.x-z2.x)*(z1.x-z2.x)+(z1.y-z2.y)*(z1.y-z2.y);
t=sqrt(t);
return t;
}
double fun(point s[],int left,int right ){
double d1,d2,d3;
int mid=(right+left)/2;
int i,j,l=left,h=right;
if(left+1==right){//只有两个点时最小值为这两个点
a=left;
b=right;
return distance(s[left],s[right]);
}
if(left+2==right){//有三个点时,需要查找,两两比较找最小值
//三个点之间的两两距离
a=left+1;
b=right;
d1=distance(s[left],s[right]);
d2=distance(s[left+1],s[right]);
d3=distance(s[left],s[left+1]);
if(d1<d2){
d2=d1;//d2为d1 d2中的最小值
a=left;
b=right;
}
if(d3<d2){
d2=d3;//d2为最小值
a=left;
b=left+1;
}
return d2;
}
//多于三个点则需要划分
d1=fun(s,left,mid);//处理左边
d2=fun(s,mid+1,right);//处理右边
if(d1>d2) d1=d2;//d1是小的值
//寻找跨中间的最小值
while(s[l].x<s[mid].x-d1&&l<=right)
l++;
while(s[h].x>s[mid].x+d1&&h>=left)
h--;
for(i=l;i<=h;i++){
for(j=i+1;j<=h;j++){
if(s[j].y-s[i].y>=d1){
break;
}else{
d3=distance(s[i],s[j]);
if(d3<d1){
d1=d3;
a=i;
b=j;
}
}
}
}
return d1;
}
int main(){
int n,i;
double d=0;
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%d %d",&z[i].x,&z[i].y);
}
d=fun(z,0,n-1);
printf("%g %d %d %d %d\n",d,z[a].x,z[a].y,z[b].x,z[b].y);
return 0;
}