例题:设平面上有n个点,,...,,n>1,的直角坐标是(,),i=1,2,...,n,求距离最近的两个点及它们之间的距离。
最短距离公式:
用蛮力算法需要计算每两个点之间的距离,并比较出最短距离,那么就有个点对,需要O()的时间。
分治算法
初步思想:用一个竖线将整个平面内p集合划分成左右两个平面和,使左右平面各有将近。
p中的最邻近点有三种情况:
- 两个点都在中
- 两个点都在中
- 一个点在中,另一个在 中
对于前两种情况,可以分别计算和 中的最邻近点对,这是两个2/n的子问题。
对于第三种情况,假设和中最邻近点对之间的距离为和,令,那么在 和 中的任意两点间距离都小于等于,这就是说,如果出现了第三种情况,那么这一点对的距离也不能超过,因此,为找到这两点,只需要考虑直线两边不超过的窄缝即可。
伪代码如下:
输入:n个点的集合P,X和Y分别给出P中点的横、纵坐标
输出:最近的两个点及距离
- 如果P中点数小于等于3,直接给出最短距离
- 排序X,Y(X:横坐标集合,Y:纵坐标集合)
- 做垂线将p近似划分为大小相等点集 和
- //递归计算左半平面最邻近点对
- //递归计算右半平面最邻近点对
- 对于在直线L左边距离范围内的每个点,检查L右边是否有点与它的距离小于,若有,更新值
该算法的时间复杂度的递推方程为:
C语言算法实现如下:
//例2.5 MinDistance(P,X,Y)
//输入:n个点的集合P,X和Y分别给出P中点的横、纵坐标
//输出:最近的两个点及距离
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define INF 2147483647
#define N 10000
struct node {
double x;
double y;
}point[N];
//调用结构体的快排
int cmp_px(const void* a, const void* b)
{
struct node aa = *(struct node*)a;
struct node bb = *(struct node*)b;
if (aa.x != bb.x)
return aa.x - bb.x;//按照x从小到大的顺序排序
else
return aa.y - bb.y;//当x相等的时候,按照y的从大到小的顺序排序
}
//计算两点的距离的函数
double distance(int start, int end)
{
return sqrt((point[start].x - point[end].x) * (point[start].x - point[end].x) + ((point[start].y - point[end].y) * (point[start].y - point[end].y)));
}
double MinDistance(int start, int end)
{
//若范围内只有一个点,则返回无穷大
if (start == end)
{
return INF;
}
//若范围中有两个点,这两个点的距离即为最小距离
else if (start == end - 1)
{
return distance(start,end);
}
//若范围内有三个点,则遍历求出两两点之间的距离比较大小
else if(start == end - 2)
{
double a,b,c;
int mid = (start + end) / 2;
a = distance(start,mid);
b = distance(start,end);
c = distance(mid,end);
int min = a;
if(b < min)
min = b;
else if(c < min)
min = c;
return min;
}
//大于三个点的时候
else
{
double mdistance;
int i = 0, j = 0, k = 0;
int mid = (start + end) / 2;
double left = MinDistance(start, mid);//左边递归
double right = MinDistance(mid + 1, end);//右边递归
mdistance = left < right ? left : right;//比较左右两边的最短距离,找出最小值
int temp[N]={ 0 };
for(i=start;i<=end;i++)
{
if(fabs(point[mid].x - point[i].x) <= mdistance)
{
temp[k++] = i;
}
}
for (i = 0; i <= k - 1; i++)//纵坐标寻找最短距离
{
for (j = i + 1; j <= k - 1 && j < i + 7; j++)
{
if (fabs(point[temp[j]].y - point[temp[i]].y) < mdistance)
{
mdistance = mdistance < distance(temp[i], temp[j]) ? mdistance : distance(temp[i], temp[j]);
}
}
}
return mdistance;
}
}
int main()
{
int n = 1;
int i;
printf("请输入集合点个数:") ;
scanf_s("%d", &n);
printf("输入原始数据:\n") ;
for (i = 0; i < n; i++)
{
scanf("%lf %lf", &point[i].x, &point[i].y);
}
qsort(point, n, sizeof(point[0]), cmp_px);
printf("排序后:\n");
for(i=0;i<n;i++)
{
printf("%lf %lf\n",point[i].x,point[i].y);
}
printf("最短距离为:%.2lf\n", MinDistance(0, n - 1));
return 0;
}
输入原始数据
P | 1 | 2 | 3 | 4 |
X | 0.5 | 2 | -2 | 1 |
Y | 2 | 3 | 4 | -1 |
运行结果: