分治算法——最邻近点对

分治算法——最邻近点对

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

解决思路

如果说用暴力的方法来解决这道题,我们需要将所有的点两两进行比较,两层循环的时间复杂度为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;
}
以下是分治算法求最邻近对的C语言代码: #include <stdio.h> #include <stdlib.h> #include <math.h> #define INF 1e9 //定义无穷大 typedef struct Point { double x, y; } point; int cmpx(const void *a, const void *b) { //按x坐标排序的比较函数 point *p1 = (point *)a; point *p2 = (point *)b; if (p1->x < p2->x) return -1; else if (p1->x > p2->x) return 1; else return 0; } int cmpy(const void *a, const void *b) { //按y坐标排序的比较函数 point *p1 = (point *)a; point *p2 = (point *)b; if (p1->y < p2->y) return -1; else if (p1->y > p2->y) return 1; else return 0; } double dist(point p1, point p2) { //计算两个之间的距离 return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2)); } double min(double x, double y) { //返回两个数中较小的数 return x < y ? x : y; } double closestPair(point *p, int n) { double d, d1, d2, d3, dmin; //d为当前最短距离,d1,d2,d3为分治后的三部分最短距离 int i, j, k, m; point *p1, *p2; if (n <= 1) return INF; //若数小于等于1,则返回无穷大 if (n == 2) return dist(p[0], p[1]); //若数等于2,则返回这两个的距离 qsort(p, n, sizeof(point), cmpx); //按x坐标排序 m = n / 2; //取中 d1 = closestPair(p, m); //递归求左半部分的最短距离 d2 = closestPair(p + m, n - m); //递归求右半部分的最短距离 d = min(d1, d2); //取左右两部分中的最短距离 point *strip = (point *)malloc(n * sizeof(point)); //分配存储跨越中线的的数组 j = 0; for (i = 0; i < n; i++) { //将横坐标距离中线小于d的复制到strip数组中 if (fabs(p[i].x - p[m].x) < d) { strip[j] = p[i]; j++; } } qsort(strip, j, sizeof(point), cmpy); //按y坐标排序 dmin = d; for (i = 0; i < j; i++) { //在strip数组中找距离不超过d的最近对 for (k = i + 1; k < j && strip[k].y - strip[i].y < dmin; k++) { d3 = dist(strip[i], strip[k]); if (d3 < dmin) { dmin = d3; p1 = &strip[i]; p2 = &strip[k]; } } } free(strip); //释放strip数组 if (dmin < d) return dmin; //返回strip数组中的最近对距离 else return d; //返回左右两部分的最近对距离 } int main() { int n, i; printf("Please input the number of points:\n"); scanf("%d", &n); point *p = (point *)malloc(n * sizeof(point)); //分配存储的数组 printf("Please input the coordinates of the points:\n"); for (i = 0; i < n; i++) { scanf("%lf%lf", &p[i].x, &p[i].y); } printf("The closest pair distance is %lf.\n", closestPair(p, n)); //输出最近对距离 free(p); //释放p数组 return 0; }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值