平面最近点距离问题(分治法)

问题描述:给定平面上n个点,每个点坐标分别为(x,y),求其中的一对点,使得在n个点组成的所有点对中,该点对的距离最小。
最简单的做法是找到所有点对的距离,然后求最小值,但这样做效率太低,时间复杂度为O(n*n), 求最小值的问题,我们很容易联想到分治法,但是分治法一般是用在一维的条件下,那么问题来了,当扩展到二维的情况下,如何使用分治法呢?

一般想到分治法,我们一般最开始可能就会想到,把n个点分成左右两部分, 为了将平面上的点集S分割为大小大致相同的2个子集S1和S2, 我们可以首先把所有点根据横坐标x从小到大排序,我们选取x=mid来作为分割线。其中,mid为S中所有点的横坐标的中位数。 这样可以将点集合S分为2部分,S1={P属于S| x§<=m} 和 S2={P属于S| x§>m}, 这样S1和S2中坐标点的个数大致相等。

然后分别求左、右两部分里面点的最短距离(设其最短距离分别为left_min和right_min),现在设d=min(left_min,right_min),若S中存在最近点对(p,q)之间的距离小于d,那么p和q必分属于S1和S2,p,q的横坐标距直线x=mid的距离也均小于d.

![这里写图片描述](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDI4MTA1ODM4OTA5?x-oss-process=image/format,png)

在一维的情况下,距分割点距离为d的2个区间(mid-d,mid)和(mid,mid+d)中最多各有一个点(若多余一个点,与左右区间最小距离为d矛盾)。在二维情况下则要稍微复杂些, 最坏情况下可能左右各有n/2个点, 那么如果我们遍历这n/2个点对,需要的时间为n*n/4, 这样做时间效率太低,显然我们不能这样做。考虑到对P1中任意一点p,若他与p2中的某个点q构成一个最短距离,设p的坐标为(x,y),那么显然q的横坐标的范围为(mid,mid+d),纵坐标范围为(y-d,y+d).

![包含点q的d*2d的矩形](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDI4MTExMzEwODM5?x-oss-process=image/format,png)

那么对于每一个点p, 与其构成最短距离的点必然在一个d2d的矩形框内(该矩形框内可能有多个点,但是任意2个点之间的距离都不超过d,接下来我们证明该矩形框中的点最多不超过一个常数),我们把该矩形框划分为6个(d/2)(2d/3)的矩形, 由于该矩形的对角线长度为 ( d / 2 ) 2 + ( 2 d / 3 ) 2 = 5 d / 6 \sqrt{(d/2)^{2}+(2d/3)^{2}}=5d/6 (d/2)2+(2d/3)2 =5d/6 小于d, 故每个矩形框内实际上最多只可能有一个这样的点, 这样对于任意一个点p,与其对应的可能构成最小距离的点最多不超过6个,这样左右之间的匹配次数最多是6n次。
我们可以把距离x=mid左右距离不超过d的所有的点按纵坐标排序,这样在找这些点时会方便一些。
这样时间复杂度T(n)=2T(n/2)+O(n), T(n)=O(n*logn)

//求平面中点之间的最短距离
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct point
{
	double x;
	double y;
};
bool sortBy_x(point& point1, point& point2) //根据x坐标从小到大排序
{
	if (point1.x == point2.x)
		return point1.y < point2.y;
	return point1.x < point2.x;
}
bool sortBy_y(point& point1, point& point2)    //根据纵坐标从小到大排序
{
	return point1.y < point2.y;
}
double getDistance(point& point1, point& point2)
{
	double dis_x = point1.x - point2.x;
	double dis_y = point1.y - point2.y;
	return sqrt(dis_x*dis_x+dis_y*dis_y);
}
double getMin(vector<point>& vec, int low, int high)
{
	if (high - low == 1)    //2个结点
	{
		return getDistance(vec[low], vec[low + 1]);
	}	
	else if (high - low == 2)        //3个结点
	{
		double dist1= getDistance(vec[low], vec[low + 1]);
		double dist2 = getDistance(vec[low], vec[low + 2]);
		double dist3 = getDistance(vec[low+1], vec[low + 2]);
		return min(min(dist1, dist2), dist3);
	}
	else
	{
		int mid = (low + high) / 2;
		double left_min = getMin(vec, low, mid);
		double right_min = getMin(vec, mid + 1, high);
		double d = min(left_min, right_min);
		vector<point> res;
		res.erase(res.begin(), res.end());  
		int i, j,k=0;
		for (i = low; i <= high; i++)        //遍历一遍数组,得到与横坐标与  中点横坐标距离在d以内的点
		{
			if (fabs(vec[i].x - vec[mid].x) < d)
				res.push_back(vec[i]);
		}
		sort(res.begin(),res.end(),sortBy_y);      //根据纵坐标从小到大排序
		for (i = 0; i < res.size() - 1; i++)
		{
			for (j = i + 1; j < res.size(); j++)  //求距离的点 为与i纵坐标的距离在d以内
			{
				if (res[j].y - res[i].y >= d)
					break;
				double dp = getDistance(res[i], res[j]);
				if (dp < d)
				{
					d = dp;
				}
			}
		}
		return d;
	}
}
int main()
{
	int i, j, n;
	cin >> n;     //输入点的个数
	vector<point> vec;
	for (i = 0; i < n; i++)
	{
		point point1;
		cin >> point1.x >> point1.y;
		vec.push_back(point1);
	}
	sort(vec.begin(), vec.end(), sortBy_x);
	cout << "所有点之间的最短距离为:" << getMin(vec, 0, vec.size() - 1) << endl;  
	system("pause");
	return 0;
}
  • 23
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值