寻找最近点对算法

寻找最近点对算法一般而言需要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;
}

点赞