分治法----最近对问题
问题描述:
- 已知有n个点的集合,找出其中最近的一对并返回其最近距离。
解题思路:
- 这道题的排序是对存入数组的下标进行操作的。
- 其思路首先是得出所要求的点的个数,再生成随机的点,然后再将所要求的点的坐标依次放入一个结构体数组中,再分别将第一次和最后一次存入的数组的数组下标作为分治法要求的左边界和右边界,再求得其中间的边界,通过递归调用左边界和右边界返回左边界和右边界中的最近点的距离(d=min(d1,d2))。
- 最近点还有可能在两个集合之间(一个在左边界一个在右边界)。且距离小于d,然后返回最小的距离。

#include "stdafx.h"
#include"math.h"
#include"stdlib.h"
#define min(x,y) (x<y)?x:y
#define MAX 10000
typedef struct node{
int x;
int y;
}z[MAX];
double distance(node z1, node z2)
{
double t;
t = (z1.x - z2.x)*(z1.x - z2.x) + (z1.y - z2.y)*(z1.y - z2.y);
return sqrt(t);
}
double division(node s[], int left, int right)
{
if (right - left == 1)
{
return distance(s[right], s[left]);
}
if (right - left == 2)
{
double d1 = distance(s[right], s[left]);
double d2 = distance(s[right], s[left + 1]);
double d3 = distance(s[left + 1], s[left]);
d2 = min(d1, d2);
d3 = min(d2, d3);
return d3;
}
int m = (right + left) / 2;
int i, j;
double d1 = division(s, left, m);
double d2 = division(s, m + 1, right);
double d = min(d1, d2);
while (d< s[m].x - s[left].x &&left <= right)
{
left++;
}
while (d < s[right].x - s[m].x&&right >= left)
{
right--;
}
double d3;
for (i = left; i <= right; i++){
for (j = i + 1; j <= right; j++)
{
if (s[j].y - s[i].y >= d){
break;
}
else {
d3 = distance(s[i], s[j]);
if (d3 < d)
d = d3;
}
}
}
return d;
}
void main()
{
struct node z[MAX];
int i, j;
int n;
printf("请输入要生成的点数:");
scanf_s("%d", &n);
if (n < 2)
{
printf("输入错误");
exit(0);
}
for (i = 0; i < n; i++)
{
z[i].x = rand() % 100 + 1;
z[i].y = rand() % 100 + 1;
for (j = 0; j < i; j++)
{
if (z[j].x == z[i].x&&z[i].y == z[j].y){
i--;
break;
}
}
}
for (i = 0; i < n; i++)
{
printf("(%d,%d) ", z[i].x, z[i].y);
}
printf("最近对最小距离为%lf\n", division(z, 0, n - 1));
}
- 运行结果:
