求最近点对问题

这篇博客探讨了如何在二维平面上寻找一组点中距离最近的两个点。文章介绍了两种方法:暴力法和分治法。暴力法通过遍历所有点对计算距离,而分治法则利用平衡子问题原则,将问题分解并递归解决。最后,通过比较不同情况下的最近点对,找到全局最小距离。代码示例展示了这两种算法的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 问题

最近对问题要求在包含有n个点的集合S中,找出距离最近的两个点。设 p1(x1,y1),p2(x2,y2),……,pn(xn,yn)是平面的n个点。严格地讲,最近点对可能不止一对,此例输出一对即可。

2. 解析

在蛮力法实现最近点对问题中,将问题简化:距离最近的点对可能多于一对,找出一对即可,另外只考虑二维平面中的情况。此处考虑到
直接用公式计算其距离
在利用分治法思想解决此问题时,首先考虑将最近对问题进行分治,设计其分治策略。将集合S分成两个子集S1和S2,根据
平衡子问题原则,每个子集中的点数大致都为n/2。这样分治后,最近点对将会出现三种情况:在S1中,在S2中或者最近
点对分别在集合S1和S2中。利用递归分析法分别计算前两种情况,第三种方法另外分析。求解出三类子情况后,
再合并三类情况,比较分析后输出三者中最小的距离。

3. 设计

#include<stdlib.h>
#include<stdio.h>
#include<iostream>
#include<math.h>
#define MAX 1000000
using namespace std;
double closestPoints(double x[], double y[], int n) {
	double x1, x2, y1, y2;
	double dist, minDist = MAX;
	for (int i = 0; i < n; i++)
		for (int j = i + 1; j < n; j++) {
			dist = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
			if (dist < minDist) {
				minDist = dist;
				x1 = x[i]; y1 = y[i];
				x2 = x[j]; y2 = y[j];
			}
		}
	cout << "最近点对为:(" << x1 << "," << y1 << ")-(" << x2 << "," << y2 << ")";
	return minDist;
}
int main() {
	double x[100], y[100];
	double minDist;
	int n;
	cout << "输入点的个数:\n";
	cin >> n;
	cout << "输入点集的坐标:\n";
	for (int i = 0; i < n; i++)
		cin >> x[i] >> y[i];
	minDist = closestPoints(x, y, n);
	cout << "其距离为:\n" << sqrt(minDist);
	return 0;
}
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
struct point {       
	double x, y;
};
double Distance(point a, point b) {
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
bool cmp(point a, point b) {       
	return a.y < b.y;
}
bool cmp2(point a, point b) {    
	return a.x < b.x;
}
double closestPoint(point s[], int low, int high, point rec[]) {
	double d1, d2, d3, d;
	int mid, i, j, index;
	double x1, y1, x2, y2;      
	point P[high - low + 1], temp1[2], temp2[2], temp3[2];   
	if (high - low == 1) {        
		rec[0].x = s[low].x; rec[0].y = s[low].y;
		rec[1].x = s[high].x; rec[1].y = s[high].y;
		return Distance(s[low], s[high]);
	}
	if (high - low == 2) {        
		d1 = Distance(s[low], s[low + 1]);
		d2 = Distance(s[low + 1], s[high]);
		d3 = Distance(s[low], s[high]);
		if ((d1 < d2) && (d1 < d3)) {
			rec[0].x = s[low].x; rec[0].y = s[low].y;
			rec[1].x = s[low + 1].x; rec[1].y = s[low + 1].y;
			return d1;
		}
		else if (d2 < d3) {
			rec[0].x = s[low + 1].x; rec[0].y = s[low + 1].y;
			rec[1].x = s[high].x; rec[1].y = s[high].y;
			return d2;
		}
		else {
			rec[0].x = s[low].x; rec[0].y = s[low].y;
			rec[1].x = s[high].x; rec[1].y = s[high].y;
			return d3;
		}
	}
	mid = (low + high) / 2;   
	d1 = closestPoint(s, low, mid, rec);
	temp1[0] = rec[0];
	temp1[1] = rec[1];
	d2 = closestPoint(s, mid + 1, high, rec);
	temp2[0] = rec[0];
	temp2[1] = rec[1];
	if (d1 < d2) {
		d = d1;
		rec[0] = temp1[0];
		rec[1] = temp1[1];
	}
	else {
		d = d2;
		rec[0] = temp2[0];
		rec[1] = temp2[1];
	}
	index = 0;
	for (i = mid; (i >= low) && ((s[mid].x - s[i].x) < d); i--)   
		P[index++] = s[i];
	for (i = mid + 1; (i <= high) && ((s[i].x - s[mid].x) < d); i++)
		P[index++] = s[i];
	sort(P, P + index, cmp);          
	for (i = 0; i < index; i++) {
		for (j = j + 1; j < index; i++) {
			if ((P[j].y - P[i].y) >= d)
				break;
			else {
				d3 = Distance(P[i], P[j]);
				if (d3 < d) {
					rec[0].x = P[i].x; rec[0].y = P[i].y;
					rec[1].x = P[j].x; rec[1].y = P[j].y;
					d = d3;
				}
			}
		}
	}
	return d;
}
int main() {
	point p[10];
	int n;
	double minDist;
	cout << "输入点的个数:\n"; 
	cin >> n;
	cout << "输入点集:(x,y)\n";
	for (int i = 0; i < n; i++)
		cin >> p[i].x >> p[i].y;
	sort(p, p + n, cmp2); 
	point index[2];
	minDist = closestPoint(p, 0, n - 1, index);
	cout << "最小距离点对为:(" << index[0].x << "," << index[0].y << "),(" << index[1].x << "," << index[1].y << ")\n";
	cout << "最小距离为:\n" << minDist;   
	return 0;
}

4. 分析

通过遍历所有点集,计算出每一个点对的距离,计算出最近的距离并输出。避免同一对点计算两次,只考虑i<j的点对(pi,pj)。
其主要循环的步骤就是求出平方值,执行的次数为
在分治算法中,当求解n个点的集合的最近点对时,对于上述三类情况中的前两者可由递归算得,
而分析可得第三类情况的时间代价 ,合并后问题求解的总的时间按复杂度可由下列公式:

5. 源码

https://github.com/Alt0191/—/blob/main/%E5%88%86%E6%B2%BB%E6%B3%95
https://github.com/Alt0191/—/blob/main/%E6%9A%B4%E5%8A%9B%E6%B3%95

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值