算法设计与分析之最邻近点对问题

问题描述:

给定平面上的n个点,找出其中的一对点,使得在n个点组成的所有点对中,该点对的距离最小。

算法思路:

看见这个问题,我们第一时间能想出的最笨的方法就是:将每一个点与其他n-1个点的距离算出,找出最小距离,可以见得复杂度很高的,那还有什么办法呢?

一维最接近点问题:

为了使问题易于理解和分析,先来考虑一维的情形。此时, S 中的 n 个点退化为 x 轴上的 n 个实数 x1,x2,…,xn 。最接近点对即为这 n 个实数中相差最小的 2 个实数。
假设我们用 x 轴上某个点 m S 划分为 2 个子集 S1 S2 基于 平衡子问题 的思想,用S中各点坐标的中位数来作分割点。
递归地在 S1 S2 上找出其最接近点对 {p1,p2} {q1,q2} ,并设 d=min{|p1-p2|,|q1-q2|} S 中的最接近点对或者是 {p1,p2} ,或者是 {q1,q2} ,或者是某个 {p3,q3} ,其中 p3∈S1 q3∈S2
如果 S 的最接近点对是 {p3,q3} ,即 |p3-q3|<d ,则 p3 q3 两者与 m 的距离不超过 d p3∈(m-d,m] q3∈(m,m+d]
由于在 S1 中,每个长度为 d 的半闭区间至多包含一个点(否则必有两点距离小于 d ),并且 m S1 S2 的分割点,因此 (m-d,m] 中至多包含 S 中的一个点。由图可以看出,如果 (m-d,m] 中有 S 中的点,则此点就是 S1 中最大点。
因此,我们用线性时间就能找到区间 (m-d,m] (m,m+d] 中所有点,即 p3 q3 。从而我们用线性时间就可以将 S1 的解和 S2 的解合并成为 S 的解。

二维最接近点问题:

下面来考虑二维的情形。
选取一垂直线 l:x=m 来作为分割直线。其中 m S 中各点 x 坐标的中位数。由此将 S 分割为 S1 S2
递归地在 S1 S2 上找出其最小距离 d1 d2 ,并设 d=min{d1,d2} S 中的最接近点对或者是 d ,或者是某个 {p,q} ,其中 p∈P1 q∈P2
  考虑P1 中任意一点 p ,它若与 P2 中的点 q 构成最接近点对的候选者,则必有 distance(p q) d 。满足这个条件的 P2 中的点一定落在一个 d×2d 的矩形 R中。由d的意义可知, P2 中任何 2 S 中的点的距离都不小于 d 。由此可以推出矩形 R 中最多只有 6 S中的点。

证明:将矩形R的长为2d的边3等分,将它的长为d的边2等分,由此导出6(d/2)×(2d/3)的矩形。若矩形R中有多于6S中的点,则由鸽舍原理易知至少有一个(d/2)×(2d/3)的小矩形中有2个以上S中的点。设uv是位于同一小矩形中的2个点,则

distance(u,v)<d。这与d的意义相矛盾

因此,在分治法的合并步骤中最多只需要检查6×n/2=3n个候选者 
为了 确切地知道要检查哪6个点 ,可以将 p P2 中所有 S2 的点投影到垂直线 l 上。由于能与 p 点一起构成最接近点对候选者的 S2 中点一定在矩形 R 中,所以它们在直线 l 上的投影点距 p l 上投影点的距离小于 d 。由上面的分析可知,这种投影点最多只有 6 个。
因此,若将 P1 P2 中所有 S 中点按其 y 坐标排好序,则对 P1 中所有点,对排好序的点列作一次扫描,就可以找出所有最接近点对的候选者。对 P1 中每一点最多只要检查 P2 中排好序的相继 6 个点。

复杂度分析:

T(n)=O(nlogn)

实现代码:

int Partition(point P[], int first, int end){
	//初始化待划分区间
	int i = first, j = end;
	while (i<j)
	{
		//右侧扫描
		while (i<j&&P[i].y <= P[j].y)
		{
			j--;
		}
 
		//将较小的记录换到前面
		if (i<j)
		{
			int temp = P[i].y;
			P[i].y = P[j].y;
			P[j].y = temp;
			i++;
		}
 
		while (i<j&&P[i].y <= P[j].y)
		{
			i++;
		}
 
		if (i<j)
		{
			int temp = P[i].y;
			P[i].y = P[j].y;
			P[j].y = temp;
			j--;
		}
	}
	return i;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值