寻找最近点对

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文是关于在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;
		}
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值