【算法分析与设计】平面最近点对(含最近距离、最近点对、第一次分割点集合的输出)

这篇博客详细介绍了如何使用递归与分治策略解决二维平面上的最接近点对问题。首先,通过对所有点的x坐标排序,找到中位数将点集分为两部分。然后,递归地计算左右子集合的最接近点对,同时检查是否存在跨集合的更近点对。最终,通过比较得到整个点集的最接近点对。博客给出了具体的算法步骤、伪代码和时间复杂度分析,并提供了C++代码实现。
摘要由CSDN通过智能技术生成

【问题描述】

给定二维平面上n个点,找其中的一对点,使得在n个点组成的所有点对中,该点对间的距离最小。使用递归与分治策略求解二维平面上的最接近点对问题。假设所有点的集合为S,m为S中所有点的x坐标的中位数,垂直线x=m将集合S均匀分割为左右两个子集合S1和S2。

[输入]:

在屏幕上输入点的个数,以及所有点的x和y坐标。

[输出]:

第一次分割时,将所有点集合S分割为左右两个子集合S1和S2,分别输出左右子集合S1和S2,以及所有点集合S的最接近点对的距离以及最接近点对。

[样例输入]:

10

-15.4 -57.3

13.2 30.1

-87.5 93.2

47.6 -12.7

94.7 61.5

56.8 -57.1

27.8 43.5

-28.1 19.0

-96.2 47.5

55.5 -93.3

[样例输出]:

42.8

-28.1 19.0

13.2 30.1

36.2

55.5 -93.3

56.8 -57.1

19.8

13.2 30.1

27.8 43.5

[样例说明]:

输入:10个点,后续每行为每一点的x和y坐标。

输出:左右子集合S1和S2,以及所有点集合S的最接近点对的距离以及最接近点对。例如,前面三行中,S1的最接近点对的距离为42.8,最接近点对的x和y坐标分别为(-28.1,19.0)和(13.2,30.1)。输出最接近点对坐标时,先输出的点的x坐标小于后输出点的x坐标。中间三行和最后三行分别为子集合S2和集合S的最接近点对的距离以及最接近点对。

[问题分析]:

平面最近点对问题属于递归与分治类问题,此问题可以从一维最近点对问题开始研究:

当我们处理一个一维最近点对问题时,通过递归与分治的思想,我们可以不断对其进行二分,通过先对一维线上的点进行排序(此步骤是为了找中位数点的时候方便),然后找到这些点中的中位数mid,然后依此中位数为划分标准,对所有的点集合进行二分,划分为左集合和右集合,以此递归,在每层递归中,分别找到划分的左右集合中的最近点对,并选出最近的那一点对,然后再检查是否存在两个点一个在左集合,一个在右集合而这两点间的距离小于刚才找到的最近点对的距离(当然前人已经证明:如果最近点对出现在这种情况,在被划分的左右集合中最多各有一个点)。

然后再将此情景推广到二维平面点集合,划分标准中位数mid则由点集合中每个点的x坐标来求,mid也可以被看作是一条垂直的线x=mid,将这个二维平面进行了划分。接下来的步骤就跟一维最近点对问题的处理方法大致相同,不同的是,在出现最近点对的两个点是一个位于左集合,一个位于右集合时,不再是在被划分的左右集合中最多各有一个点,而是可能在左边出现n/2个点,右边最多出现6个点(这个结论前人也已经进行了证明)。

[算法步骤]:

1.将所有点按照x的进行排序

2.用x=mid将整个平面划分为两部分

3.求出x<=mid部分的最近点对距离d_left,求出x>mid部分的最近点对距离d_right,求出这两者中最小的距离记为d

4.找出所有x坐标在(mid-d,mid+d)范围中的点,放入res

5.对于(x,y),x∈(mid-d,mid),如果点(x1,y1)中x1>mid且|y-y1|<d,那么记录两点间距离,如果小于d,那么更新d,否则检查下一个点,以此类推,直至遍历完res中所有的点。

[伪代码]:

if(r-l==1) return dis(node[l],node[r])

if(r-l==2) return min(dis(node[l],node[r]),min(dis(node[l],node[l+1]),dis(node[l+1],node[r])))

else

        sort(node,node+n,by_X)

        mid=(l+r)/2

        d=min(closet(node,l,mid),closet(node,mid+1,r))

        for i=l to r

                if(fabs(node[i].x-node[mid].x)<d) res.push_back(node[i])

        sort(res.begin(),res.end(),by_Y)

        for i=0 to res.size

                for j=i+1 to res.size

                        if(fabs(res[i].y-res[j].y)<d)

                                min_dis=dis(res[i],res[j])

                                if(min_dis<d) d=min_dis

[时间复杂度分析]:

T(n)=1           当k=2时

T(n)=2T(n/2)+n   当k>2时

所以T(n)=O(nlog2n)

[代码实现]:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<bits/stdc++.h>
using namespace std;
typedef struct Point
{
	double x,y;
}Point;

Point node[1000];
int n;

double dis(Point a,Point b)
{
	return sqrt(pow((a.x-b.x),2)+pow((a.y-b.y),2));
}

bool by_X(Point a,Point b)//按x大小排序 
{
	if(a.x==b.x) return a.y<b.y;
	else return a.x<b.x;
}

bool by_Y(Point a,Point b)//按y大小排序 
{
	if(a.y==b.y) return a.x<b.x;
	else return a.y<b.y;
}


void swap(Point &a,Point &b)
{
	Point c=a;
	a=b;
	b=c;
}
double closet(int l,int r,Point *best_node)
{
	
	if(r-l==1)//两个点的情况 
	{
		best_node[0]=node[l];
		best_node[1]=node[r]; 
		return dis(node[l],node[r]);
		
	}
	else if(r-l==2)//三个点的情况 
	{
		double dis1=dis(node[l],node[l+1]);
		double dis2=dis(node[l+1],node[r]);
		double dis3=dis(node[l],node[r]);
		if(dis1<dis2&&dis1<dis3)
		{
			best_node[0]=node[l];
			best_node[1]=node[l+1];
			return dis1;
		}
		if(dis2<dis1&&dis2<dis3)
		{
			best_node[0]=node[l+1];
			best_node[1]=node[r];
			return dis2;
		}
		if(dis3<dis2&&dis3<dis1)
		{
			best_node[0]=node[l];
			best_node[1]=node[r];
			return dis3;
		}
	}
	else//大于三个点的情况 
	{	
		int mid=(l+r)/2;
		Point temp1[2],temp2[2];
		double d_left=closet(l,mid,best_node);
		temp1[0]=best_node[0];
		temp1[1]=best_node[1];
		if(mid==(0+n-1)/2) //为了保证是在最外一层递归(即第一次分割的时候)的时候再输出 
		{
			printf("%.1lf\n",d_left);
			if(temp1[0].x>temp1[1].x) swap(temp1[0],temp1[1]);
			printf("%.1lf %.1lf\n",temp1[0].x,temp1[0].y);
			printf("%.1lf %.1lf\n",temp1[1].x,temp1[1].y);
		 } 
		double d_right=closet(mid+1,r,best_node);
		temp2[0]=best_node[0];
		temp2[1]=best_node[1];
		if(mid==(0+n-1)/2) 
		{
			printf("%.1lf\n",d_right);
			if(temp2[0].x>temp2[1].x) swap(temp2[0],temp2[1]);
			printf("%.1lf %.1lf\n",temp2[0].x,temp2[0].y);
			printf("%.1lf %.1lf\n",temp2[1].x,temp2[1].y);
		 }
		double d;
		if(d_left<=d_right)
		{
			d=d_left;
			best_node[0]=temp1[0];
			best_node[1]=temp1[1];
		}
		if(d_left>d_right)
		{
			d=d_right;
			best_node[0]=temp2[0];
			best_node[1]=temp2[1];
		 } 
		vector<Point> res;
		for(int i=l;i<=r;i++)//将距离mid距离小于d的点放入res 
		{
			if(fabs(node[i].x-node[mid].x)<d)
			{
				res.push_back(node[i]);
			}
		}
		sort(res.begin(),res.end(),by_Y);//将res中的点按照y的大小进行排序,便于后面的筛选 
		for(int i=0;i<res.size();i++)//通过i,j遍历res中所有的点对,从而找到距离最近的点对 
		{
			for(int j=i+1;j<res.size();j++)
			{
				if(fabs(res[i].y-res[j].y)>d) break;
				double min_dis=dis(res[i],res[j]);
				if(min_dis<d)
				{
					d=min_dis;
					best_node[0]=res[i];
					best_node[1]=res[j];
				}
			}
		}	
		return d;	
	}
	
}
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n;
	double d;
	for(int i=0;i<n;i++)
	{
		cin>>node[i].x;
		cin>>node[i].y;
	}	
	Point best_node[2];//记录最近点对 
	sort(node,node+n,by_X);
	d=closet(0,n-1,best_node); 
	printf("%.1lf\n",d);
	if(best_node[0].x>best_node[1].x) swap(best_node[0],best_node[1]);
	printf("%.1lf %.1lf\n",best_node[0].x,best_node[0].y);
	printf("%.1lf %.1lf",best_node[1].x,best_node[1].y);
 } 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值