1. 算法思路
时间复杂度 : O(n⋅logn)
该算法采用分治法,问题被分解为两个子问题,合并的复杂度为 O(n),所以:T(n)=2⋅T(2n)+n
即时间复杂度为:O(n⋅logn)
2. 示例代码
2.1 import
1 2 3 4 5
| import java.util.ArrayList; import java.util.Collections; import java.util.Comparator;
import primitives.Point;
|
2.2 分治法
2.2.1 外部方法
1 2 3 4 5 6 7 8 9
| public static ArrayList<Point> Fast(ArrayList<Point> points) { ArrayList<Point> X = (ArrayList<Point>)points.clone(); ArrayList<Point> Y = (ArrayList<Point>)points.clone();
Collections.sort(X, new PointComparatorX()); Collections.sort(Y, new PointComparatorY());
return ClosestPair(X, Y); }
|
暴露给外部进行调用的方法,先对点集分别按X和Y坐标排序,分成两个序列
2.2.2 递归方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| private static ArrayList<Point> ClosestPair(ArrayList<Point> X, ArrayList<Point> Y) { ArrayList<Point> resultPair = new ArrayList<Point>();
if (X.size() <= 1) { resultPair = null; } else if (X.size() == 2) { resultPair = X; } else if (X.size() == 3) { double dist1 = dist(X.get(0), X.get(1)); double dist2 = dist(X.get(0), X.get(2)); double dist3 = dist(X.get(1), X.get(2));
if (dist2 < dist3) { if (dist1 < dist2) { resultPair.add(X.get(0)); resultPair.add(X.get(1)); } else { resultPair.add(X.get(0)); resultPair.add(X.get(2)); } } else { if (dist1 < dist3) { resultPair.add(X.get(0)); resultPair.add(X.get(1)); } else { resultPair.add(X.get(1)); resultPair.add(X.get(2)); } } } else { double lX = (X.get(X.size() / 2)).getX();
ArrayList<Point> XL = new ArrayList<Point>(); ArrayList<Point> XR = new ArrayList<Point>();
for (int i = 0; i < X.size(); i++) { if ((X.get(i)).getX() <= lX) { XL.add(X.get(i)); } else { XR.add(X.get(i)); } }
ArrayList<Point> YL = new ArrayList<Point>(); ArrayList<Point> YR = new ArrayList<Point>();
for (int i = 0; i < Y.size(); i++) { if ((Y.get(i)).getX() <= lX) { YL.add(Y.get(i)); } else { YR.add(Y.get(i)); } } if (XR.size() == 0) { Point repl = XL.get(XL.size()-1); XL.remove(repl); XR.add(repl); YL.remove(repl); YR.add(repl); }
ArrayList<Point> ClosestLeft = ClosestPair(XL, YL); ArrayList<Point> ClosestRight = ClosestPair(XR, YR); ArrayList<Point> ClosestBetween = ClosestBetweenSubsets(X, Y, Math.min(dist(ClosestLeft), dist(ClosestRight)));
double distLeft = dist(ClosestLeft); double distRight = dist(ClosestRight); double distBetween = dist(ClosestBetween);
if (distLeft < distRight) { if (distBetween < distLeft) { resultPair = ClosestBetween; } else { resultPair = ClosestLeft; } } else { if (distBetween < distRight) { resultPair = ClosestBetween; } else { resultPair = ClosestRight; } } }
return resultPair; }
|

1. 按 X 坐标进行排序,获得点序列 X
,取序列的中位数 lX
- 将 [0 , lX]
的点分别加入点集 XL
和 YL
- 将 [lX , size - 1]
的点分别加入点集 XR
和 YR
;
2. 左右子集分别调用 ClosestPair()
进行递归,分别获得左右子集中的最近点对,其距离分别为 distLeft
和 distRight
3. 取 distLeft
和 distRight
的最小值 distMin
,即 distMin = min { distLeft, distRight }
4. 调用 ClosestBetweenSubsets()
方法,取中线左右间距 distMin
区域内的点加入点集 Y2
,找到其中的最近点对,距离为 distBetween
5. 结果取 resultPair = min{distLeft, distRight, distBetween}
L |
R |
Y2 |
P0,P1,P2,P3 |
P4,P5,P6 |
P2,P3,P4,P5 |
- 其中:
distMin = distLeft
,区域取中线左右间距 distMin
,获得点集 Y2
- 调用
ClosestBetweenSubsets()
方法后,找到最近点对 P3−P4 和间距 distBetween
- 最后
resultPair = distBetween
2.2.3 子集间最接近的点对
对于每个点只需要检查水平间距最近的 8 个点即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| private static ArrayList<Point> ClosestBetweenSubsets(ArrayList<Point> X, ArrayList<Point> Y, double d) { double lX = (X.get(X.size() / 2)).getX();
ArrayList<Point> Y2 = new ArrayList<Point>(); for (int i = 0; i < Y.size(); i++) { if (Math.abs((Y.get(i)).getX() - lX) <= d) { Y2.add(Y.get(i)); } }
if (Y2.size() < 2) { return null; }
Point p1 = Y2.get(0); Point p2 = Y2.get(1);
for (int i = 0; i < Y2.size() - 1; i++) { for (int j = i + 1; j <= Math.min(i + 8, Y2.size() - 1); j++) if (dist(Y2.get(i), Y2.get(j)) < dist(p1, p2)) { p1 = Y2.get(i); p2 = Y2.get(j); } }
ArrayList resultPair = new ArrayList(); resultPair.add(p1); resultPair.add(p2);
return resultPair; }
|
2.3 比较器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| static class PointComparatorX implements Comparator<Point> {
@Override public int compare(Point p1, Point p2) { if (p1.getX() < p2.getX()) return -1; if (p1.getX() > p2.getX()) return 1; return 0; } }
static class PointComparatorY implements Comparator<Point> { @Override public int compare(Point p1, Point p2) { if (p1.getY() < p2.getY()) return -1; if (p1.getY() > p2.getY()) return 1; return 0; } }
|
2.4 其他方法
2.4.1 计算点对的间距
1 2 3 4 5 6 7 8 9 10
| private static double dist(ArrayList<Point> pair) { if (pair == null || pair.size() != 2) { return Double.POSITIVE_INFINITY; } else { return Math.sqrt(((pair.get(1)).getX() - (pair.get(0)).getX()) * ((pair.get(1)).getX() - (pair.get(0)).getX()) + ((pair.get(1)).getY() - (pair.get(0)).getY()) * ((pair.get(1)).getY() - (pair.get(0)).getY())); } }
|