寻找最近点对算法一般而言需要O(n^2)的时间复杂度,即枚举法,分别计算每两个点对之间的距离,取最小。 但是还有一种分治算法理论上可以将时间复杂度减小到O(n.log n)的级别。但是由于递归的开销,可能效果并不一定好,具体还要看问题特点。
算法描述:
①若问题规模小于3,直接枚举法求解。
②否则,将点集按照x座标排序,取中间点。
③以中间点的xm座标为边界,左右分别递归求解,得到左右最短距离
④取左右两边递归结果的最小值a,取出x座标在xm-a到xm+a之间的点集
⑤对于中间区域的点集,按照y座标排序
⑥对于每一个点,依次计算y座标比它大的7个点,计算最小距离。
⑦将中间区域的最小距离与a比较,取最小值返回。
下面是java代码:
/**
* @param pointList
* @return 最短距离
* @description 使用分治算法寻找最短点对的距离
*/
private static double findNearestPair(List<Point> pointList, int start, int end) {
if (end – start <= 3) { //当点的个数小于三个的时候,直接求解
return simpleNearestPair(pointList);
}
Collections.sort(pointList, new PointXComparator()); //按照x排序
int mid = (end + start) / 2;
Point midPoint = pointList.get(mid);
double res1 = findNearestPair(pointList, start, mid);
double res2 = findNearestPair(pointList, mid, end);
double res3 = Math.min(res1, res2);
double res = -1;
//构造中间条状区域的点集合
ArrayList<Point> pointByY = new ArrayList<>();
for (int i = start; i < end; i++) {
if (Math.abs(pointList.get(i).x – midPoint.x) <= res3) {
pointByY.add(pointList.get(i));
}
}
//对于每个点状区域的点,计算可能的跨越左右两边的最小值
Collections.sort(pointList, new PointYComparator()); //按照x排序
for (int k = 0; k < pointByY.size(); k++) {
int num = 0;
for (int i = k + 1; i < pointByY.size(); i++) {
if (num >= 7) {
break;
}
double tmp = pointByY.get(k).calDis(pointByY.get(i));
if (tmp < res || Math.abs(res + 1) < 0.000001) {
res = tmp;
}
}
}
return (Math.abs(res + 1) > 0.000001 && res < res3) ? res : res3;
}
/**
* @param pointList
* @return
* @description 朴素寻找最短距离方法
*/
private static double simpleNearestPair(List<Point> pointList) {
double res = -1;
for (Point p1 : pointList) {
for (Point p2 : pointList) {
if (p1 == p2) {
continue;
}
double dis = p1.calDis(p2);
if (Math.abs(res + 1) < 0.000001 || res > dis) {
res = dis;
}
}
}
return res;
}