最近点对

1.问题

最近点对问题:在一个平面上随机分布着 n 个点,现给定 n 个点的坐标,要求给出最近的两个点之间的距离。

2.解析

1.暴力算法
在双重for暴力的情况下,该问题的时间复杂度为O(n²),当 n 的值不断增大时,这个方法处理起来就显得效率很低。
2.分治法
分治法的思想是将平面上的点分成左右两半,分别求左右两半的最小距离,然后取一个最小值,整个问题的规模为n个点,将这n个点一分为二,就变成两个求解n/2个点规模下最近点对的距离问题,如此不断缩小规模,当变成两个点的规模下求解最近点对问题时,即为这两个点的距离。当然,最近点对还可能会有这样的情况,即一个点在左半边,一个点在右半边。

分解:
对所有的点按照x坐标从小到大排序(排序方法时间复杂度O(nlogn))。
根据下标进行分割,使得点集分为两个集合。
解决:
递归的寻找两个集合中的最近点对。
取两个集合最近点对中的最小值dis。
合并:
最近距离不一定存在于两个集合中,可能一个点在集合A,一个点在集合B,而这两点	间距离小于dis。
其中合并的这一步具体展开:
我们假设从两部分分别求出的最近距离为 dis1 和 dis2 , dis = min( dis1, dis2 ),再假设分属两部分的情况下,A ( x1 , y1 ) 属于第一部分 ,B ( x2 , y2 )属于第二部分,且以 x 轴作为分割的量,我们可以断定,如果存在解,A 、B 两点一定处在中心分割线两侧 2*dis 长度之内的区间内,且经过相关证明,在这范围内的点不会超过6个。

在这里插入图片描述

3.设计

float closest(Point* points, int n, Point& a, Point& b) {
	//递归函数出口,两个点直接返回距离,一个点时返回无穷大
	if (n == 2) 
		return distance(a, b);
	else if (n == 1) {
		return INF;
	}
	else {//分治
		int n1 = n / 2;//前一半
		int n2 = n - n1;//后一半
		//左右两个分区 分别存入points1和points2
		for (遍历左半边) 
			points1[i] = points[i];
		for (遍历右半边) 
			points2[i] = points[i + n1];
		d1 = closest(points1, n1, a1, b1);//递归得到左半边最小距离d1
		d2 = closest(points2, n2, a2, b2);//递归得到右半边最小距离d2
			dis = min(d1, d2);
		//中间区域
		for (遍历1到n) {
			if (fabs(points[i].x - mid) <= dis) {//若到中轴距离小于dis
				points3[++k] = points[i];//存入points3
			}
		}
		for (遍历1到k) {
			for (最多遍历6个点) {
				if (distance(points3[i], points3[j]) < dis) {
					dis = distance(a, b);
				}
			}
		}
		return dis;
	}
}

4.分析

快排的时间为O(nlogn),而遍历的时间小于O(nlogn),于是计算后得到整体时间复杂度为O(nlogn)。

在这里插入图片描述


版权声明:本文为zoeyuanyuan原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。