本文是关于在n>=2个点的集合Q中寻找最近点对的问题。
容易想到的暴力手段,需要遍历theta(n^2)个点对,效率不高。
本文中给出一种时间复杂度为O(nlgn)的算法,算法采用分治策略。下面具体描述:
输入为P、X和Y,其中P是Q的子集,数组X包含P的所有点,并按照x坐标单调递增,同样Y中的点按照y坐标排序。为了维持O(nlgn)的时间界,不能在每次递归调用中都排序,否则运行时间递归式就变为T(n) = 2T(n/2)+O(nlgn),解为O(n(lgn)^2)。
加入|P|<=3,仅执行暴力方法;如果|P|>3,执行分治策略:
将P平分为两个部分(P_l和P_r),并分别递归调用,求出P_l和P_r的最近点对,并取delta为其中较小的一个。
那么最终的结果就有可能是delta,但是也有可能有一个点在P_l,一个点在P_r,且两点之间的距离小于delta。
这样的两个点必定处于以直线l(l将P分为P_l和P_r)为中心,宽度为2×delta的区域内。为了找出这样的点,我们需要建立一个数组 Y' 来保存所有在前述区域内的点,让后将其按照y坐标排序。对数组Y'内的每个点p,只需要考察紧随其后的7个点,从中记录最小的点对距离delta'。如果delta'小于delta,则返回delta'及其点对;否则返回delta。
关于为什么只要考察p之后的7个点,这里简单说明。首先一个长为delta的正方形,其中最多能放4个点使得每两个点之间的距离不小于delta,这个用反证法正比较好证。因为目前是两个这样的正方形,并且两个正方形中可以有两队重叠的点,因此最多有8个点。
Java代码如下:
import java.util.*;
public class Solution {
static final int N = 100005;
static Point[] points = new Point[N];
static Integer[] area = new Integer[N];
static{
}
public static double closest_pair(int l,int r){
double d = Double.MAX_VALUE;
if(l == r) return d;
if(l+1 == r) return dis(l,r);
int mid = (l+r)>>1;
double ld = closest_pair(l,mid);
double rd = closest_pair(mid+1,r);
d = min(ld,rd);
int k = 0;
for(int i = l;i<=r;i++){
if(Math.abs(points[i].x - points[mid].x) <= d)
area[k++] = i;
}
Arrays.sort(area,0,k,new Comparator<Integer>(){
public int compare(Integer a,Integer b){
if(points[a].y == points[b].y) return 0;
if(points[a].y < points[b].y) return -1;
return 1;
}
});
for(int i = 0;i < k;i++){
for(int j = i+1;j < k && j<i+8;j++){
double dij = dis(area[i],area[j]);
if(dij < d) d = dij;
}
}
return d;
}
public static void main(String[] args){
System.out.println("请输入点的个数:");
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
init(n);
System.out.println("请输入点的坐标:");
for(int i = 0;i < n;i++){
points[i].x = cin.nextDouble();
points[i].y = cin.nextDouble();
}
Arrays.sort(points,0,n);
System.out.println("closest pair distance:"+closest_pair(0,n-1));
}
public static void init(int n){for(int i = 0;i < n;i++) {points[i] = new Point(); area[i] = 0;}}
public static double dis(int i,int j){
return Math.sqrt((points[i].x-points[j].x)*(points[i].x-points[j].x)+
(points[i].y-points[j].y)*(points[i].y - points[j].y));
}
static double min(double a,double b){
return a < b? a : b;
}
static class Point implements Comparable<Point>{
double x;
double y;
public int compareTo(Point p){
if(x != p.x) return x < p.x ? -1 : 1;
return y == p.y?0:(y < p.y)?-1:1;
}
}
}

本文探讨了在大量点集中寻找最近点对的算法,通过分治策略实现O(nlgn)的时间复杂度。当点集大小小于等于3时采用暴力方法,否则平分点集并递归查找,结合特定区域内的点进行进一步筛选,以找到可能的最近点对。文章还提供了Java代码示例。
2万+

被折叠的 条评论
为什么被折叠?



