最近点对问题中涉及到对点按x或y进行升序排序,笔者尝试对排序方式进行优化。首先想到的自然是归并排序,毕竟归并排序的核心思想也是分治法,但是归并排序比较适合基数较多的情况。最后笔者更改思路如下:当点集合总数n的值小于50时,采用插入排序,当n的值大于50时采用归并排序,代码如下:
void MergeSort(point *a, int p, int r,bool sort_x_y)
{
if ((r-p)>=50) // 小于50个数据的数组进行插入排序
{
int q = (p+r)/2;
MergeSort(a, p, q,sort_x_y);
MergeSort(a, q+1, r),sort_x_y;
Merge(a, p, q, r,sort_x_y);
}else
{
InsertionSort(a+p, r-p+1,sort_x_y);
}
}
double ClosestPoint(point s_array[],int low,int high,point min_point[]){
double d1,d2,d3,d;
int mid,i,j,index;
double x1,y1,x2,y2; //记录点对的位置
point P[high-low+1],temp1[2],temp2[2]; //辅助空间
if(high-low==1){ //两个点的情况
min_point[0].x=s_array[low].x;min_point[0].y=s_array[low].y;
min_point[1].x=s_array[high].x;min_point[1].y=s_array[high].y;
return Points_Distance(s_array[low],s_array[high]);
}
if(high-low==2){ //三个点的情况
d1=Points_Distance(s_array[low],s_array[low+1]);
d2=Points_Distance(s_array[low+1],s_array[high]);
d3=Points_Distance(s_array[low],s_array[high]);
if((d1<d2)&&(d1<d3)){
min_point[0].x=s_array[low].x;min_point[0].y=s_array[low].y;
min_point[1].x=s_array[low+1].x;min_point[1].y=s_array[low+1].y;
return d1;
}
else if(d2<d3){
min_point[0].x=s_array[low+1].x;min_point[0].y=s_array[low+1].y;
min_point[1].x=s_array[high].x;min_point[1].y=s_array[high].y;
return d2;
}
else {
min_point[0].x=s_array[low].x;min_point[0].y=s_array[low].y;
min_point[1].x=s_array[high].x;min_point[1].y=s_array[high].y;
return d3;
}
}
mid=(low+high)/2; //其他情况递归
d1=ClosestPoint(s_array,low,mid,min_point);
temp1[0]=min_point[0];
temp1[1]=min_point[1];
d2=ClosestPoint(s_array,mid+1,high,min_point);
temp2[0]=min_point[0];
temp2[1]=min_point[1];
if(d1<d2){
d=d1;
min_point[0]=temp1[0];
min_point[1]=temp1[1];
}
else {
d=d2;
min_point[0]=temp2[0];
min_point[1]=temp2[1];
}
index=0;
for(i=mid;(i>=low)&&((s_array[mid].x-s_array[i].x)<d);i--) //点集合p1
P[index++]=s_array[i];
for(i=mid+1;(i<=high)&&((s_array[i].x-s_array[mid].x)<d);i++) //点集合p2
P[index++]=s_array[i];
MergeSort(P,P+index,Assist_y); //升序排列
for(i=0;i<index;i++){
for(j=j+1;j<index;i++){
if((P[j].y-P[i].y)>=d)
break;
else {
d3=Points_Distance(P[i],P[j]);
if(d3<d){
min_point[0].x=P[i].x;min_point[0].y=P[i].y;
min_point[1].x=P[j].x;min_point[1].y=P[j].y;
d=d3;
}
}
}
}
return d;
}
int main(){ //设定点的集合
int n,dimension;
double min_distance;
cout<<"输入点的个数:\n"; //输入点的个数
cin>>n;
point s_array[n];
L1: cout<<"请输入点所在的位面(一维输入1,二维输入2):";
cin>>dimension;
if(dimension==1)
for(int i=0;i<n;i++)
{
cout<<"请输入第"<<i+1<<"个点距离原点的距离"<<endl;
cin>>s_array[i].x;
s_array[i].y=0;
}
else if(dimension==2)
for(int i=0;i<n;i++)
{
cout<<"请输入第"<<i+1<<"个点的坐标(用空格隔开)"<<endl;
cin>>s_array[i].x>>s_array[i].y;
}
else
{
cout<<"输入位面不合法"<<endl;
goto L1;
}
MergeSort(s_array,s_array+n,Assist_x);
point min_point[2];
min_distance=ClosestPoint(s_array,0,n-1,min_point);
cout<<"最小距离点对为:("<<min_point[0].x<<","<<min_point[0].y<<"),("<<min_point[1].x<<","<<min_point[1].y<<")";
cout<<"最小距离为:\n"<<min_distance;
return 0;
}
笔者没有给InsertionSort()和Merge()函数,因为这两个函数的代码量比预想中的大,按照x/y排序需要进行判断,需要要进行函数重载。在这里笔者给大家推荐sort()函数,只需用#include <algorithm> sort即可使用,它使用的排序方法是类似于快排的方法,时间复杂度为n*log2(n),执行效率较高。
比如在上述笔者的代码中要实现只需要加两个函数如下:
bool Assist_y(point a,point b){ //按y升排序
return a.y<b.y;
}
bool Assist_x(point a,point b){ //按x升排序
return a.x<b.x;
}
调用:
sort(s_array,s_array+n,Assist_x);//按x升序
sort(s_array,s_array+n,Assist_y);//按y升序