- 问题
[描述算法问题,首选形式化方式(数学语言),其次才是非形式化方式(日常语言)]
对于平面上给定的N个点,给出所有点对的最短距离,即,输入是平面上的N个点,输出是N点中具有最短距离的两点 - 解析
[问题的理解和推导,可用电子版直接在此编写,也可用纸笔推导,拍照嵌入本文档]
将集合S分成两个子集S1和S2,每个子集中有n/2个点。然后在每个子集中递归地求其最接近的点对,在求出每个子集的最接近点对后,有两种可能情况:1集合S 中最接近的两个点都在子集S1或S2中2这两个点分别在S1和S2中。⑵对于第一点递归求解,如果最近点对分别在集合S1和S2中,那就分别取s1s2中最靠近中线的点,与中位点两两比较,并与子集S1或S2的最小路径对比,得出答案 - 设计
[核心伪代码]
double divide_conquer(int start, int end, myPoint* X, myPoint* Y, myPoint& P1, myPoint& P2) {
if (end - start < 3) //当点的数量小于等于3时,用暴力法。由于是按照x坐标来划分区域,固X数组的点的数量和位置君未发生改变,故用X数组不用Y数组
return force(start, end, X,P1,P2);
int mid = (start + end) / 2; //按照x坐标的排序,从中间切开
int leftLen = 0, rightLen = 0, i, j; //leftLen,和rightLen分别指示左右两边点集的个数
myPoint* YL = new myPoint[(end - start + 1) ]; //保存中线左边的点集,按照Y坐标排序
myPoint* YR = new myPoint[(end - start + 1) ]; //保存中线右边的点集,按照Y坐标排序
for (i = 0; i <= end - start; i++) { //遍历集合中每一个点,他们都是按照y的顺序进行排序的,若点的x坐标小于等于中线,则分割到左子集,否则分割到右子集
//通过此操作YL和YR中的点的坐标依旧是按照y坐标排序好的
if (Y[i].x <= X[mid].x) {
YL[leftLen++] = Y[i];
}
else {
YR[rightLen++] = Y[i];
}
}
double left = divide_conquer(start, mid, X, YL,P1,P2); //递归求左边部分的最短距离
myPoint leftP1 = P1; //保存左支距离最小的两个点的坐标
myPoint leftP2 = P2;
double right = divide_conquer(mid + 1, end, X, YR,P1,P2); //递归求右边部分的最短距离
double minDis; //取小的一个
if (left < right) {
minDis = left;
P1 = leftP1;
P2 = leftP2;
}
else { //此时并不需要更新P1,P2的值,因为此时P1和P2的值就是右支的最小的一对点的坐标
minDis = right;
}
myPoint* newY = new myPoint[(end - start + 1)]; //新建一个数组,用于保存以中界线为中心,宽度为 2*minDis 的垂直带形区域内的点
int newYLen = 0;
double leftBorder = X[mid].x - minDis; //区域的左边界
double rightBorder = X[mid].x + minDis; //区域的右边界
for (i = 0; i <= end - start; i++) { //遍历Y集中所有点,满足条件则加入新集合,新集合的点的依然按照y坐标排序好
if (Y[i].x >= leftBorder && Y[i].x <= rightBorder)
newY[newYLen++] = Y[i];
}
for (i = 0; i<newYLen; i++) { //在新集合中,判断每个点跟它后面的7个点的距离作比较,若小于则更新最小距离
for (j = 1; j <= 7; j++) {
if ((i + j)<newYLen) { //加入条件,防止越界
double dis = Distance(newY[i], newY[i + j]);
if (dis < minDis) {
minDis = dis;
P1 = newY[i];
P2 = newY[i + j];
}
}
}
}
delete YL;
delete YR;
delete newY;
return minDis;
}
4. 分析
[算法复杂度推导]
假设n是2的幂
算法运行时间的递归式T(n)=2T(n/2)+f(n)
因此算法时间复杂度为O(nlogn)
5. 源码
[github源码地址]
https://github.com/zddhandsome/Algorithm/blob/main/%E5%88%86%E6%B2%BB%E6%B3%95%E6%9C%80%E8%BF%91%E5%AF%B9%E9%97%AE%E9%A2%98.cpp