问题
平面最近点对问题是指:在给出的同一个平面内的所有点的坐标,然后找出这些点中最近的两个点的距离.
暴力法
//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; }
拆分成左右两份
//如果大于2,3,可以再次拆分 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
//leftList,rightList递归 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)); } //如果大于2,3,可以再次拆分 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)); } //leftList,rightList递归 double d1 = conquer(leftList); double d2 = conquer(rightList); //递归出来的结果取最小值 double d = Math.min(d1, d2); //x为中点坐标,leftX,rightX划分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; } }