【分治法】最近点对问题(一维+二维)

本文介绍了如何使用分治法解决一维和二维平面上的最近点对问题。通过将点集分割,递归求解子集的最近点对,最后合并得到全局最近点对。在一维情况下,通过中位数分割,二维情况同样采用中位数分割,限制搜索范围。算法时间复杂度为O(nlog2n),优于O(n^2)的蛮力方法。
摘要由CSDN通过智能技术生成

问题描述

设p1=(x1, y1), p2=(x2, y2), …, pn=(xn,yn)是平面上n个点构成的集合S,最近对问题就是找出集合S中距离最近的点对。
严格地讲,最接近点对可能多于一对,简单起见,只找出其中的一对作为问题的解。

求解思路

用分治法解决最近对问题,很自然的想法就是将集合S分成两个子集 S1和 S2,每个子集中有n/2个点。然后在每个子集中递归地求其最接近的点对,在求出每个子集的最接近点对后,在合并步中,如果集合 S 中最接近的两个点都在子集 S1或 S2中,则问题很容易解决,如果这两个点分别在 S1和 S2中,问题就比较复杂了。
一维的情况
此时,S中的点退化为x轴上的n个点x1, x2, …, xn。用x轴上的某个点m将S划分为两个集合S1和S2,并且S1和S2含有点的个数相同。递归地在S1和S2上求出最接近点对 (p1, p2) 和(q1, q2),如果集合S中的最接近点对都在子集S1或S2中,则d=min{(p1, p2), (q1, q2)}即为所求,如果集合S中的最接近点对分别在S1和S2中,则一定是(p3, q3),其中,p3是子集S1中的最大值,q3是子集S2中的最小值。
在这里插入图片描述
按这种分治策略求解最近对问题的算法效率取决于划分点m的选取,一个基本的要求是要遵循平衡子问题的原则。如果选取m=(max{S}+min{S})/2,则有可能因集合S中点分布的不均匀而造成子集S1和S2的不平衡,如果用S中各点坐标的中位数作为分割点,则会得到一个平衡的分割点m,使得子集S1和S2中有个数大致相同的点。
假设我们用x轴上的某个点m将S划分为两个集合S1和S2 ,使得S1={x∈S | x≤m} ;S2 ={ x∈S | x>m }。这样一来,对于所有p∈ S1和q∈S2 有p<q。递归地在S1和S2 上找出其最接近点对{p1,p2}和{q1,q2},并设 d=min{| p1- p2|,| q1- q2|},如果S的最接近点对是{p3,q3},即|p3-q3|<d,则p3和q3两者与m的距离不超过d,即| p3-m|<d,| q3 -m|<d。也就是说 ,p3∈(m-d,m],q3∈(m,m+d]。
由于每个长度为d的半闭区间至多包含S1中的一个点,并且m是S1和S2 的分割点,由图可以看出,如果(m-d,m]中有S中点 ,则此点就是S1中最大点。同理,如果(m,m+d]中有S中点,则此点就是S2 中最小点。因此,我们用线性时间就能找到区间(m-d,m]和(m,m+d]中所有点,即p3和q3。从而我们用线性时间就可以将S1的解和S2 的解合并成为S的解。
二维的情况
为了将平面上的点集S 分割为点的个数大致相同的两个子集S1和S2,选取垂直线x=m来作为分割线,其中,m为S中各点x坐标的中位数。由此将S分割为S1={p∈S | xp≤m}和S2={q∈S | xq>m}。递归地在S1和S2上求解最近对问题,分别得到S1中的最近距离d1和S2中的最近距离d2,令d=min(d1, d2),若S的最近对(p, q)之间的距离小于d,则p和q必分属于S1和S2,不妨设p∈S1,q∈S2,则p和q距直线x=m的距离均小于d,所以,可以将求解限制在以x=m为中心、宽度为2d的垂直带P1和P2中,垂直带之外的任何点对之间的距离都一定大于d。
在这里插入图片描述
对于点p∈P1,需要考察P2中的各个点和点p之间的距离是否小于d,显然,P2中这样点的y轴坐标一定位于区间[y-d, y+d]之间,而且,这样的点不会超过6个。
在这里插入图片描述

应用分治法求解含有n个点的最近对问题,其时间复杂性可由下面的递推式表示:
合并子问题的解的时间f(n)=O(n),T(n)=O(nlog2n),蛮力法求解最近点对问题,时间复杂度为O(n2)。

代码实现

#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<conio.h>
#include<graphics.h>

#define MIN(a,b) ((a.dis)>(b.dis)?(b):(a))
#define fabs(a) ((a)<(0)?(-(a)):(a))

//用于记录一维的最近点对
typedef struct{
   
	double a, b;
	double dis;
}record;

//二维的点
struct point
{
   
	int x, y;
};

//求二维的距离函数
double Distance(point a, point b){
   
	return sqrt((double)((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y)));
}

//一维的比较排序函数
/*int cmp(const void *x, const void *y){
	double a, b;
	a = *((double *)x);
	b = *((double *)y);
	return a > b;
}*/
//划分
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--;
		}
	}
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值