A.算法设计原理
1.采用分治的策略,将问题以X的中位数划分为左右俩个子问题分别为minleft=MinDistance(PL,XL,YL),minright= MinDistance(PR,XR,YR)。每个子问题求解得到的值,取较小值d= min{minleft,minright}
2.假设在第一步中找到的最小的距离为d,我们只需要再分别从左右取点p,q,得到距离与d相比较,d=min{d,getDistance(p,q)}。关键在于证明是有限个点。推导过程如下(图像反了)
B.伪代码描述:
- 初始化P,X,Y
- 快速排序P(按照x),X,Y,Y在排序前得到对应x得位置MinDistance(P,X,Y)
- 若P中点的数目为2,3直接求解
- 根据最中间得下标直接划分P,X,根据Y已知对应x的下标拆分X,得到PL,XL,YL,PR,XR,YR;
求minleft= MinDistance(PL,XL,YL)
minright= MinDistance(PR,XR,YR)
d=min(dr,dl) - 在PL距离范围内的每个点,检查PR中是否有点与该点的距离小于d,若有则d取新值。由于上面的拆分其实此时每个子问题Y是有序的,我则从小到大检查,每一个点后继的6个点即可
- 返回d
C.关键问题
1.拆分点集,关键是让Y中的y知道当前情况下的X的位置,所以需要设计一个特殊的数据结构来记录。
struct Pointxy
{
int number;
int myindex;//索引号
int otherindex;//用于y知道对应得x在X中的位置,也是在P中的位置
};
在X中数据排好序后,y马上记录下来此时x的位置
for (int i = 1; i <= listnumber; i++)
{
Y[X[i].myindex].otherindex= i;
}
由于每次排好序X的x之间的位置是不变的,它只是通过中位线划分了,所以我们只要对Y中记录的x坐标做一个相对变换就好了
void DividXY()
{
int mid = n / 2;
int xl = 0, xr = 0, yl= 0, yr = 0;
for (int i = 1; i <= n; i++)
{
if (i <= mid)
{
xl++;
XL[xl] = X[i];
}
else
{
xr++;
XR[xr] = X[i];
}
}
for (int j = 1; j <= n; j++)
{
if (Y[j].otherindex <=mid)
{
yl++;
YL[yl] = Y[j];
}
else
{
yr++;
YR[yr] = Y[j];
YR[yr].otherindex -=mid;
}
}
}
//划分P
void DividP(Numlist *P, Numlist *PL, Numlist *PR)
{
int m = 0, n = 0;
int mid = P->number / 2;
for (int i = 1; i <= P->number; i++)
{
if (i <= mid)
{
m++;
PL->point[m] = P->point[i];
}
else
{
n++;
PR->point[n] = P->point[i];
}
}
PL->number = m;
PR->number = n;
}
关键函数
float MinDistance(Numlist *P, Pointxy X[], Pointxy Y[])
{
float d;
int n = P->number;
if (n == 2)
{
d =getDistance(P->point[n], P->point[n - 1]);
}
else if (n == 3)
{
float d1 = getDistance(P->point[n], P->point[n - 1]);
float d2 = getDistance(P->point[n], P->point[n - 2]);
float d3 = getDistance(P->point[n - 1], P->point[n - 2]);
d = min(d1,min(d2, d3));
}
else
{
int Mid = n / 2;
Numlist *PL = new Numlist, *PR = new Numlist;
Pointxy XL[MAX], XR[MAX];
Pointxy YL[MAX], YR[MAX];
DividP(P, PL, PR);
DiviXY(n, X, Y, XL, YL, XR, YR);
double minleft =MinDistance(PL, XL, YL);
double minright =MinDistance(PR, XR, YR)
d =min(minleft, minright);
//此时Y为有序
for (int i = 1; i <= n;i++)
{
int k = (i + 7) >(n) ? (n) : (i + 7);//从第一个点开始取后面的7个点
for (int j = i + 1; j <=
k; j++)//只比较往后的6位数
{
int index1 = Y[i].otherindex;//对应P的下标
int index2 = Y[j].otherindex;
if (Y[j].number - Y[i].number >= d)//大于d立即返回
break;
else
{
d= min(d, getDistance(P->point[index1], P->point[index2]));
}
}
}
}
return d;
}
对P,X,Y初始化,以及用快速排序排好传参就可以输出最小点对的距离值。