最近点对问题算法
最近点对问题是一种很典型的递归分治算法,我们知道,递归分治的核心步骤就是
分
治
合
这里我们怎么分,怎么治,怎么合,便是一个值得探讨的问题。
先给出应用的结构体和主函数:
struct node{
double x;
double y;
}X[100001],tmp[100001];
int main()
{
int N,i;
scanf("%d",&N);
for(i = 0; i < N; i++)
{
double x,y;
scanf("%lf%lf",&x,&y);
X[i].x = x;
X[i].y = y;
}
qsort(X,N,sizeof(X[0]),comp_x);//先将点对按x的大小排序
double ans = closest(0,N-1);
printf("输入点对中距离最近的两点距离为%.2f",ans);
}
本算法的核心在于closest函数,通过这个函数的递归分治,可以大事化小,小事化更小。
double closest(int l, int r)
{
if(l + 1 == r)
return distance(X[l],X[r]);
if(l + 2 == r)
return min(distance(X[l],X[l+1]),min(distance(X[l+1],X[r]),distance(X[l],X[r])));
//只要输入点对大于2,就“分”:
int mid = (l + r) / 2;
double left_d = closest(l,mid);
double right_d = closest(mid + 1,r);
d = min(left_d,right_d);//从这一步开始“合”:
//具体的合的过程即是把中间处的最小距离和左右求得的最小距离相比较
int i,j,a = 0;
for(i = l; i <= r; i++)
{
if(fabs(X[i].x - X[mid].x) < d)//把符合初步条件(横坐标与中位线距离小于d的点挖出来,存储在tmp结构体中)
{
tmp[a].x = X[i].x;
tmp[a++].x = X[i].y;
}
}
qsort(tmp,a,sizeof(tmp[0]),comp_y);//按照y的大小对tmp结构进行排序
for(i = 0; i < a; i++)
{
for(j = i + 1; j < a; j++)//这里之所以敢用两层循环,是因为循环中的break,根据“鸽巢原理/抽屉原理”,一次我们最多找8个点就会break掉了,所以敢放心地用嵌套两层循环
{
if(tmp[j].y - tmp[i].y >= d)
break;
d = min(d,distance(tmp[i],tmp[j]));
}
}
return d;
}
具体的原理就是分治合,分治原理的算法就是要弄清楚这样的分治合过程。
---------------------------------------分割线----------------------------------------------
最后补充一下两个快排的comp函数:
int comp_x(const void *a, const void *b)
{
return (* (node *)a).x > (* (node *)b).x;
}
int comp_y(const void *a, const void *b)
{
return (* (node *)a).y > (* (node *)b).y;
}