分治法寻找最邻近的点对

来源:互联网 发布:网通网络测速 编辑:程序博客网 时间:2024/05/28 15:07


问题描述:


在二维平面内给定n个点,寻找这些点中举例最近的两个点;


思路:


我们发现如果要将全部的点两两比较,则最起码需要O(n^2),因此我们的思路是如果要求与某个点A距离最近的点,则需要缩小范围,不用每个点都比较;


我们需要使用分治法来求解;

首先我们需要定义一些变量:

Px:对于点按照x坐标排序;

Py:对于点按照y坐标排序;

Qx:对于左部分的点按照x坐标排序;

Qy:对于部分的点按照y坐标排序;

Rx:对于右部分的点按照x坐标排序;

Ry:对于右部分的点按照y坐标排序;






递归的出口就是如果只有三个点以内,则直接两两计算距离,并返回最小距离;


我们可以从上面清晰的看出:每次将问题分割成两个小问题,因此T(n) = 2*T(n/2)+f(n);

f(n)是问题分隔和组合的消耗;

分隔时需要计算Qx,Qy,Rx,Ry,因此花费O(n)

组合时已经计算出了Q和R区域的最小距离,d = min(d1,d2)我们需要计算一个点在Q,一个点在R的最小距离,我们需要做如下步骤:

(1)对于分隔Q和R的线,向左和向右拓宽d,落在这个区域中的点都有可能是最小距离的点对,记为S; O(n)

(2)我们对S按照y排序后为Sy,对于Sy中每个点只需要计算与他的x、y坐标相差 2*d之内的点(最多15个点); O(n)

(3)计算出S区域的最小距离后与d比较,取较小者;O(1)


因此f(n) = O(n)

T(n) = 2*T(n/2) + O(n)  ===>   T(n) = O(nlogn)


代码:


import java.util.ArrayList;import java.util.Arrays;import java.util.Comparator;public class Test {public static void main(String[] args) {initializePoint();Point[]result = closest_pair(P);dist = distance(result);System.out.println("距离最近的两点为:");print(result);System.out.println("距离为:"+dist);}private static double dist;private static Point[] P;private static Comparator<Point> ycompar = new Comparator<Point>() {  //根据y坐标比较的比较器@Overridepublic int compare(Point o1, Point o2) {if(o1.y<o2.y){return -1;}else if(o1.y>o2.y){return 1;}return 0;}};private static Comparator<Point> xcompar = new Comparator<Point>() {  //根据x坐标比较的比较器@Overridepublic int compare(Point o1, Point o2) {if(o1.x<o2.x){return -1;}else if(o1.x>o2.x){return 1;}return 0;}};//初始化点数组/** * (0,0) * (5,8) * (9,5) * (10,20) * (100,100) */private static void initializePoint() {P = new Point[5];for(int i=0;i<P.length;i++){P[i] = new Point();}P[0].x = 10;P[0].y = 20;P[1].x = 0;P[1].y = 0;P[2].x = 100;P[2].y = 100;P[3].x = 5;P[3].y = 8;P[4].x = 9;P[4].y = 5;}//主函数public static Point[] closest_pair(Point[] points) {Point[] px = points.clone();Arrays.sort(px,xcompar);Point[] py = points.clone();Arrays.sort(py,ycompar);Point[] result = closest_pair_rec(px,py);return result;}public static Point[] closest_pair_rec(Point[]px,Point[]py){Point minFirstPoint = null;Point minSecondPoint = null;double mindistance = 0;if(px.length<=3){//出口double mindist = Double.MAX_VALUE;int mini = 0;int minj = 0;for(int i=0;i<px.length;i++){for(int j=i+1;j<px.length;j++){if(mindist>distance(new Point[]{px[i],px[j]})){mindist = distance(new Point[]{px[i],px[j]});mini = i;minj = j;}}}return new Point[]{px[mini],px[minj]};}Point[] Q = Arrays.copyOfRange(px, 0, px.length/2+1);Point[] R = Arrays.copyOfRange(px, px.length/2+1, px.length);Point[] Qx = Q.clone();Arrays.sort(Qx, xcompar);Point[] Qy = Q.clone();Arrays.sort(Qy, ycompar);Point[] Rx = R.clone();Arrays.sort(Rx, xcompar);Point[]Ry = R.clone();Arrays.sort(Ry, ycompar);Point[] q = closest_pair_rec(Qx,Qy);Point[] r = closest_pair_rec(Rx,Ry);double qd = distance(q);double rd = distance(r);double delta = Math.min(qd,rd);if(delta==qd){minFirstPoint = q[0];minSecondPoint = q[1];}else{minFirstPoint = r[0];minSecondPoint = r[1];}mindistance = delta;int maxx = Qx[Qx.length-1].x;//分割线//找到与分割线距离小于delta的部分点放到S中ArrayList<Point> Stmp = new ArrayList<Point>();for(int i=0;i<px.length;i++){if(Math.abs(px[i].x-maxx)<delta){Stmp.add(px[i]);}}Point[]S = Stmp.toArray(new Point[0]);Point[] Sy = S.clone();Arrays.sort(Sy,ycompar);//寻找15个点之内的点,并比较距离,找出最小距离的得点 ,每个点至多比较8个点,因此复杂度为O(8n)double secondMinDistance = Double.MAX_VALUE;Point tmpminFirst = null;Point tmpminSecond = null;for(int i=0;i<Sy.length;i++){Point basep = Sy[i];for(int j=i+1;j<Sy.length;j++){Point compp = Sy[j];if((compp.y-basep.y < 2*delta)&&Math.abs(compp.x-basep.x)<2*delta){//如果在15个点之内,即x和y坐标相差都在2*delta之内double tmpdis = distance(new Point[]{basep,compp});if(secondMinDistance>tmpdis){secondMinDistance = tmpdis;tmpminSecond = compp;tmpminFirst = basep;}}else{break;}}}if(secondMinDistance<mindistance){return new Point[]{tmpminFirst,tmpminSecond};}else{return new Point[]{minFirstPoint,minSecondPoint};}}private static double distance(Point[] q) {return Math.pow(Math.pow(q[0].x-q[1].x,2)+Math.pow(q[0].y-q[1].y,2), 0.5);}private static void print(Point[]ps){for(Point p:ps){System.out.println("x="+p.x+",y="+p.y);}}}class Point{int x;int y;}