分治算法——最邻近点对

2023-11-12

分治算法——最邻近点对

设计与实现查找平面上的最邻近点对问题的算法。

解决思路

如果说用暴力的方法来解决这道题,我们需要将所有的点两两进行比较,两层循环的时间复杂度为O(n),那么如何降低时间复杂度呢?如果空间中只存在一个点,那么就不存在最近点对,如果空间中只有两个点时,最近点对就是这两个点,那么当空间中有三个点时,我们便可以将空间划分为两个部分,左半部分有两个点,右半部分有一个点,那么我们就可以将问题划分为求左半部分,右半部分以及交界处的最近点对的最小值。可是当点越来越多的时候,我们应该如何求得两个子控件交界处的最近点对呢?我们首先需要将随机生成的点对进行排序,之后我们才可以讨论如何对空间进行划分,假设我们用x=mid将空间划分为了两个部分,并且我们已经求得了左半部分最小点对间的距离为d1,右半部分最近点对间的距离,我们还需要找到交界处的最近点对。我们刚刚得到了d1与d2,如果我们不需要考虑交界处的点,那么最近点对间的距离就是dmin = min(d1,d2),我们可以利用这个dmin来划分一下交界处的范围,即对于任意一个点(x,y),当fabs(x-min)<=dmin时,这个点才属于交界处,才在我们需要考虑的范围内,如果当fabs(x-min)>dmin,那么该点与另一空间中的点的距离一定大于dmin。划定范围后我们就可以用暴力的方法找到交界处的最近点对,并与dmin进行比较得到最近点对。通过递归的方法我们就可以得到整个数组的最近点对
在这里插入图片描述

算法代码

struct Point
{
	int x, y;
	bool operator< (const Point& p)const
	{
		return x < p.x || (x == p.x && y < p.y);
	}
	bool operator== (const Point& p)const
	{
		return x == p.x && y == p.y;
	}
};

struct Pair
{
	int flag1, flag2;
};

double distance(Point p1, Point p2) 
{
	return sqrt(pow(p1.y - p2.y,2) + pow(p1.x - p2.x,2));
}

double CloestPair(Point points[], int length)
{
	sort(points, points + length);
	return Cloest(points, 0, length - 1);
}

//仅返回最近点对的距离的代码
double Cloest(Point points[], int start, int end)//仅返回最近点对间距离的算法
{
	if (start == end)//如果空间中只有一个点,则返回double类型的最大值
		return DBL_MAX;
	if (end - start == 1)
		return distance(points[start], points[end]);
	int mid = (start + end) / 2;
	double left = Cloest(points, start, mid);//求出左半部分的最近点对间的距离
	double right = Cloest(points, mid + 1, end);//求出右半部分的最近点对间的距离
	double mindis = min(left, right);
	Point *temp = new Point[end - start + 1];//用于记录处于交界范围内的点
	int k = 0;
	for (int i = start; i <= end; i++)//找到处于交界范围的点
		if (fabs(points[mid].x - points[i].x) <= mindis)
			temp[k++] = points[i];
	for (int i = 0; i < k; i++)//计算交接范围内的最近点对间的距离
		for (int j = i + 1; j < k; j++)
		{
			if (temp[j].y - temp[i].y > mindis)continue;
			mindis = distance(temp[i], temp[j]) < mindis ? distance(temp[i], temp[j]) : mindis;
		}
	delete[] temp;
	return mindis;
}

//返回最近点对的代码
Pair Cloest(Point points[], int start, int end,int length)//返回最近点对下标的算法
{
	Pair res = { 0, length-1 };
	//将res的默认值设置为0、len-1,因为数组是经过排序的,所以这样能保证默认的点对间聚利时最大的
	if (start == end)
		return res;//如果空间中只有一个点,那么就返回数组中距离最大的点对
	if (end - start == 1)
	{
		res.flag1 = start, res.flag2 = end;
		return res;
	}
	int mid = (start + end) / 2;
	double dis;
	Point t = { -1,-1 };//用来处理在比较过程中被抛弃的点的异常值
	Pair left = Cloest(points, start, mid, length);//左半边的最近点对
	Pair right = Cloest(points, mid + 1, end, length);//右半边的最近点对
	//得到左右两部分的最近点对
	if (distance(points[left.flag1], points[left.flag2]) <= distance(points[right.flag1], points[right.flag2]))
		res = left, dis = distance(points[left.flag1], points[left.flag2]);
	else 
		res = right, dis = distance(points[right.flag1], points[right.flag2]);
	Point *temp = new Point[end - start + 1];//用于记录属于交界处范围的点
	int k = 0;
	//扫描整个数组,找出属于交界处范围内的点
	for (int i = start; i <= end; i++, k++)
		//如果该点与中间点之间的横坐标之差小于等于最小距离,则记录下该点,否则抛弃该点,用异常值代替
		if (fabs(points[mid].x - points[i].x) <= dis)
			temp[k] = points[i];
		else
			temp[k] = t;
	for (int i = 0; i < k; i++)
		for (int j = i + 1; j < k; j++)
		{
			//如果两点之间的纵坐标之差大于最小距离,或任意一个点为异常值,则进入下一次循环
			if (temp[j].y - temp[i].y > dis || temp[i] == t || temp[j] == t)continue;
			int tmpdis = distance(temp[i], temp[j]);
			if (tmpdis < dis)//如果两点距离小于最小距离,记录下这两个点
			{
				dis = tmpdis;
				res.flag1 = i + start;
				res.flag2 = j + start;
				//由于这两个点存放在新的数组即temp中,所有我们最终需要返回他们在原数组中的真实位置
			}
		}
	delete[] temp;
	return res;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

分治算法——最邻近点对 的相关文章

随机推荐