最邻近点对问题

文章描述了一种算法,通过分治法解决给定n个二维坐标点的问题,找到最短距离的两点对。通过排序、划分区间并搜索跨区间的最邻近点,实现复杂度从O(n^2)降低到O(n)。
摘要由CSDN通过智能技术生成

问题:给定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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值