问题

平面最近点对问题是指:在给出的同一个平面内的所有点的坐标,然后找出这些点中最近的两个点的距离.

暴力法

//1.暴力法
private static double force(List<Point> list) {

    int size = list.size();

    double distance;
    double min = Double.MAX_VALUE;

    for (int i = 0; i < size; i++) {
        for (int j = i + 1; j < size; j++) {

            distance = dis(list.get(i), list.get(j));

            if (distance < min) {
                min = distance;
            }
        }
    }

    return min;
}
分治法

先按照x坐标排序(冒泡法)

//分治前先排序
private static List<Point> bubbleSort(List<Point> list) {

    int size = list.size();

    //原理就是依次浮到顶端,for循环中体现为依次把最小的浮到底端
    for (int i = 0; i < size; i++) {
        for (int j = i + 1; j < size; j++) {

            Point a = list.get(i);
            Point b = list.get(j);

            if (a.x > b.x) {
                Collections.swap(list, i, j);
            }
        }
    }

    //我觉得肯定不可以按Y再排序依次,假如左右有两个最高点,近乎一样高

    //分治:找左,找右,找小于d
    //找左中的 左,右,小于d;右中的 左,右小于d。。。
    return list;
}

拆分成左右两份

//如果大于23,可以再次拆分
List<Point> leftList = new ArrayList<>();
List<Point> rightList = new ArrayList<>();

//前半部分的点给leftList
for (int i = 0; i < size / 2; i++) {
    leftList.add(list.get(i));
}

//后半部分的点给rightList,总是可能多一个
for (int i = size / 2; i < size; i++) {
    rightList.add(list.get(i));
}

递归“求出”左右最小值d

//leftListrightList递归
double d1 = conquer(leftList);
double d2 = conquer(rightList);

//递归出来的结果取最小值
double d = Math.min(d1, d2);
左右两边处理好了,中间其实也有可能的,但是处于中轴附近d距离的才有可能比d更小

算法 分治 平面最近点对

中轴线x

当左右点数相等,就是左末点和右初点的中点;

当左比右少一个,直接就是中点,即右末。

//如果左右数目相等,取左末,右初中点为x
if (leftList.size() == rightList.size()) {
    x = (list.get(size/2 - 1).x + list.get(size/2).x) / 2;
}
//如果右边多一个,取中点为x
else {
    x = leftList.get(size/2 - 1).x;
}
中间范围

leftX = x - d;
rightX = x + d;

在中轴的左右d距离的点,就是中区域,放入新集合

List<Point> midList = new ArrayList<>();

//把集合中在mid区间的点都放进mid集合
for (int i = 0; i < size; i++) {
    if (leftX <= list.get(i).x && rightX >= list.get(i).x) {
        midList.add(list.get(i));
    }
}
暴力求出中间最小值d3,因为中间需要求的点在最坏情况下都是很少的

//暴力求出mid集合最小值
double d3 = force(midList);
和d比较,取最小

//比较
if (d3 < d) {
    d = d3;
}



完整代码

public class Test {

    static List<Point> list = new ArrayList<>();

    public static void main(final String[] args) throws Exception {

        initData();

        double resultForce = force(list);
        System.out.println(resultForce);

        bubbleSort(list);
        double resultConquer = conquer(list);
        System.out.println(resultConquer);
    }

    private static void initData() {

        Point p1 = new Point(1, 2);
        Point p2 = new Point(33, 7);
        Point p3 = new Point(9, 6);
        Point p4 = new Point(1000, 2);
        Point p5 = new Point(-505, 41);
        Point p6 = new Point(13, 12);
        Point p7 = new Point(18, 15);
        Point p8 = new Point(11, 2);
        Point p9 = new Point(-1000, -1000);

        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);
        list.add(p5);
        list.add(p6);
        list.add(p7);
        list.add(p8);
        list.add(p9);
    }

    private static double dis(Point a, Point b) {
        return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
    }

    //1.暴力法
    private static double force(List<Point> list) {

        int size = list.size();

        double distance;
        double min = Double.MAX_VALUE;

        for (int i = 0; i < size; i++) {
            for (int j = i + 1; j < size; j++) {

                distance = dis(list.get(i), list.get(j));

                if (distance < min) {
                    min = distance;
                }
            }
        }

        return min;
    }

    //分治前先排序
    private static List<Point> bubbleSort(List<Point> list) {

        int size = list.size();

        //原理就是依次浮到顶端,for循环中体现为依次把最小的浮到底端
        for (int i = 0; i < size; i++) {
            for (int j = i + 1; j < size; j++) {

                Point a = list.get(i);
                Point b = list.get(j);

                if (a.x > b.x) {
                    Collections.swap(list, i, j);
                }
            }
        }

        //我觉得肯定不可以按Y再排序依次,假如左右有两个最高点,近乎一样高

        //分治:找左,找右,找小于d
        //找左中的 左,右,小于d;右中的 左,右小于d
        return list;
    }

    //分治(传进来一个已经按x排序过的list    private static double conquer(List<Point> list) {

        int size = list.size();

        //如果传进来一个list(不知道有没有被拆解过的,无所谓的)
        //3,暴力求
        if (size == 3) {
            return force(list);
        }

        //2,直接返回距离
        if (size == 2) {
            return dis(list.get(0), list.get(1));
        }

        //如果大于23,可以再次拆分
        List<Point> leftList = new ArrayList<>();
        List<Point> rightList = new ArrayList<>();

        //前半部分的点给leftList
        for (int i = 0; i < size / 2; i++) {
            leftList.add(list.get(i));
        }

        //后半部分的点给rightList,总是可能多一个
        for (int i = size / 2; i < size; i++) {
            rightList.add(list.get(i));
        }

        //leftListrightList递归
        double d1 = conquer(leftList);
        double d2 = conquer(rightList);

        //递归出来的结果取最小值
        double d = Math.min(d1, d2);

        //x为中点坐标,leftXrightX划分mid区间
        double x;
        double leftX;
        double rightX;

        //如果左右数目相等,取左末,右初中点为x
        if (leftList.size() == rightList.size()) {
            x = (list.get(size/2 - 1).x + list.get(size/2).x) / 2;
        }
        //如果右边多一个,取中点为x
        else {
            x = leftList.get(size/2 - 1).x;
        }

        leftX = x - d;
        rightX = x + d;

        List<Point> midList = new ArrayList<>();

        //把集合中在mid区间的点都放进mid集合
        for (int i = 0; i < size; i++) {
            if (leftX <= list.get(i).x && rightX >= list.get(i).x) {
                midList.add(list.get(i));
            }
        }

        //暴力求出mid集合最小值
        double d3 = force(midList);

        //比较
        if (d3 < d) {
            d = d3;
        }

        return d;
    }
}

相关文章: