分治算法——平面点问题(使用数组实现)

目录

题目:二维最接近点对问题

暴力算法:

算法原理:

算法复杂度:

分治算法:

第一版:

算法原理:

算法复杂度:

C代码:

第二版:

算法原理:

算法复杂度:

C代码:


题目:二维最接近点对问题

【问题描述】

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

【输入形式】

第一行一个整数n,表示点的个数。

接下来n行,每行两个整数x,y,表示一个点的行坐标和列坐标。

1<=n<=1e5,0<=x,y<=1e9。

【输出形式】

仅一行,一个实数,表示最短距离,四舍五入保留2位小数。

【样例输入】

4
0 0
0 1
1 0
1 1

【样例输出】

1.00

老师要求:递归算法里不可以出现排序!!!!!!!!!!!!

暴力算法:

算法原理:

求出每两个点之间的距离,选出最小的

算法伪码:

输入:点集P()(一个二维数组,例:P[0][0]代表第一个点的x,P[0][1]代表第一个点的y)

1.定义最小距离dist为第一个点到第二个点的距离

2.两层for循环遍历更新dist的值

3.返回dist

算法复杂度:

时间复杂度:O(n^{2})

空间复杂度:C_{n}^{2} 由于本题n<1e5,C_{n}^{2}<C_{1e5}^{2}=4,999,950,000 

double baoli(double a[][2],int n) {//暴力算法
	
	int dist = getdist(a[0],a[1]);
	for (int i = 0; i < n; i++) {
		for (int j = i+1; j < n; j++) {
			dist =smaller( dist,getdist(a[i], a[j]));
		}
		
	}
	return dist;
}

分治算法:

这里转载一篇个人讲的蛮好的文章:平面点对问题(分治法的经典问题)_给定n个点,其坐标为 ( xi,yi ) ( 0£i£n-1 ) ,要求使用分治策略求解,设计算-CSDN博客

第一版:

算法原理:

把点集P划分为L和R(L、R大小相等),分别计算L和R中最近点对的距离,计算L和R中距离最近点对的距离。

算法伪码:

Mindistance(P,l,r)

输入:点集P(二维数组,P[i][0]代表第i个点的x,P[i][1]代表第i个点的y), l和r分别为点集P(数组)的左下标和右下标

输出:最近点对的距离d

1.if 点少于三个 直接计算并比较

2.根据X值的大小对P进行排序(个人选用)

3.做中垂线m(m=(l+r)/2),把点集P划分为L和R(L、R大小相等)

4.d1=Mindistance(P,m,r)

5.d2=Mindistance(P,l,m)

6.d=d1和d2中较小的值

7.计算m两侧不超过d范围内的点(理论上不会超过6个 )之间的最近距离d3。若d3小于d,更新d=d3。

算法复杂度:

时间复杂度:O(nlogn)                                                                                                                 

空间复杂度:O\left ( n\right )

试了好多遍,老是有一个测试用例不过,于是放出第二个纠错大招,输出数据,以查看其变化过程。我错的是感叹号部分。提醒一下自己注意细节!!!!!

C代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include <cmath> 
using namespace std; 

int Partition(double a[][2], int l, int r) {//初始化待划分区间
	int i = l, j = r;
	double pivot0 = a[l][0];//挖坑法  pivot坑
	double pivot1 = a[l][1];
	//cout<< "in Partition1" << endl;///
	//for (int i = l; i <= r ; i++) {
	//	cout<< "a[" << i << "][0]:" << a[i][0] << " " << "a[" << i << "][1]:" << a[i][1] << endl;
	//}
	while (i < j)
	{		//右侧扫描
		while (i < j && a[j][0]>= pivot0)
		{
			j--;//找到小的
		}
		if (i < j)//左小于右
		{
			double tempx = a[i][0];
			a[i][0] = a[j][0];
			a[j][0] = tempx;
			double tempy = a[i][1];
			a[i][1] = a[j][1];
			a[j][1] = tempy;			
			i++;
		}

		while (i < j && a[i][0] <= pivot0)
		{
			i++;
		}
		if (i < j)
		{
			double tempx = a[i][0];
			a[i][0] = a[j][0];
			a[j][0] = tempx;
			double tempy = a[i][1];
			a[i][1] = a[j][1];
			a[j][1] = tempy;
			j--;
		}
	}
	//cout<< "in Partition2" << endl;
	//for (int i = l; i <= r ; i++) {
	//	cout<< "a[" << i << "][0]:" << a[i][0] <<" "<< "a[" << i << "][1]:" << a[i][1] << endl;
	//	//cout << "a[i][0]: " << a[i][0] << " " << "a[i][1]: " << a[i][1] << endl;
	//}
	a[i][0] = pivot0;
	a[i][1] = pivot1;
	//cout << "in Partition3" << endl;/
	//for (int i = l; i <= r ; i++) {
	//	cout << "a[" << i << "][0]:" << a[i][0] << " " << "a[" << i << "][1]:" << a[i][1] << endl;
	//}
	return i;
}

//进行快速排序
void qst(double a[][2], int l, int r) {	

	//cout << "in qst1" << endl;
	//for (int i = l; i <= r; i++) {
	//	cout << "a[" << i << "][0]:" << a[i][0] << " " << "a[" << i << "][1]:" << a[i][1] << endl;
	//}
	if (l < r)
	{
		double pivot = Partition(a ,l, r);
		qst(a, l, pivot - 1);
		qst(a, pivot + 1, r);
	}
}

double smaller(double a,double b) {
	return a<b ? a : b;
}
double getdist(double a[], double b[]) {
	double ans = sqrt(pow(a[0] - b[0], 2) + pow(a[1] - b[1], 2));
	return ans;
}
double MinDistance(double a[][2],int l ,int r ) {
	int n = r+1 - l;//右+1-左
	int m = (l+r)/2;
	double distance = 0.0;//
	 if (n == 2) {//两个点
		distance = getdist(a[0], a[1]);
	}
	else if (n == 3) {//三个点
		double d1= getdist(a[0], a[1]);//点0,1
		double d2= getdist(a[2], a[1]);//点2,1
		double d3= getdist(a[0], a[2]);//点0,2
		double d4 = smaller(d1, d2);
		distance = smaller(d4, d3);
	}
	else { //划分
		int lb=m,rb=m;
		double e1 = MinDistance(a,l,m );
		double e2 = MinDistance(a,m,r);
		distance = smaller(e1, e2);
		while (lb>=l && abs(a[lb][0]-a[m][0])<distance) { //从中垂线向两边找区间
			lb--;//m--
		}
		while (rb<=r && abs(a[rb][0] - a[m][0]) < distance) {
			rb++;//m++
		}
		lb += 1;
		rb -= 1;
		for (int i = lb; i <= rb-1; i++) {
			for (int j = i + 1; j <= rb;j++) {
				if (a[i][1] - a[j][1] <=distance) {//德尔塔y<mindistance
					distance = smaller(getdist(a[i], a[j]), distance);
				}
			}
		}
	}
	return distance;
}
int main() {     //
	int n;
	double d = 0.0;
	double a[100000][2] = { 0 };//a[][0]x,a[][1]是y
	cin >> n;
	for (int i = 0; i < n; i++) {
		scanf("%lf %lf",&a[i][0],&a[i][1]);
	}
	
	qst(a,0,n-1);//n元素个数 !!!!!!!!!!n-1
	//for (int i = 0; i < n; i++) {
	//	cout << "a[" << i << "][0]:" << a[i][0] << " " << "a[" << i << "][1]:" << a[i][1] << endl;
	//}
	//cout << endl;///
	d=MinDistance(a, 0, n-1);
	printf("%.2f\n", d);
	
	return 0;
}


第二版:

算法原理:

把点集P划分为L和R(L、R大小相等),分别计算L和R中最近点对的距离,计算L和R中距离最近点对的距离。

算法伪码:

Mindistance(P,l,r)

1.输入:点集P(二维数组,P[i][0]代表第i个点的x,P[i][1]代表第i个点的y,P[i][2]代表第i个点按Y值大小排序的顺序), l和r分别为点集P(数组)的左下标和右下标,并将P[i][2]赋值为i

2.将数组P按照x的大小进行升序排序(使用快排)

3.将数组P按照y的大小进行升序排序(使用快排)得到的顺序保存在P[i][2]

4.调用Mindistance

if 点少于三个 直接计算并比较

做中垂线m(m=(l+r)/2),把点集P划分为L和R(L、R大小相等)

d1=Mindistance(P,m,r)

d2=Mindistance(P,l,m)

d=d1和d2中较小的值

从m分别向左和向右找距离m两侧不超过d的点,得到左边界lb和右边界rb

遍历,用数组b有序存放中线右边的y,

遍历中线左边的点,定义lk为b索引,初始化为0,以b[lk]做索引,遍历m和rb之间的点(由原理可知,符合条件右边点的数目小于6,遍历次数小于6),计算中线左边的点和右边点之间的距离d3。若d3小于d,更新d=d3。

输出:最近点对的距离d

算法复杂度:

时间复杂度:O\left ( nlog n\right )                                                                                                                 

空间复杂度:O\left ( n\right )

C代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include <cmath> 
using namespace std; 
void swap(double& a, double& b) {
	double temp = a;
	a = b;
	b = temp;
}
int Partition1(double a[][3], int l, int r) {//初始化待划分区间
	int i = l, j = r;
	double pivotx = a[l][0];//挖坑法  pivot坑简化版
	double pivoty = a[l][1];
	
	
	while (i < j)
	{		//右侧扫描
		while (i < j && a[j][0]>= pivotx)
		{
			j--;//找到小的
		}
		if (i < j)//左小于右
		{
			swap(a[i][0], a[j][0]);
			swap(a[i][1], a[j][1]);
			i++;
		}

		while (i < j && a[i][0] <= pivotx)
		{
			i++;
		}
		if (i < j)
		{
			swap(a[i][0], a[j][0]);
			swap(a[i][1], a[j][1]);
			j--;
		}
	}
	a[i][0] = pivotx;
	a[i][1] = pivoty;

	

	return i;
}


//进行快速排序
void qst1(double a[][3], int l, int r) {	

	
	if (l < r)
	{
		double pivot = Partition1(a ,l, r);
		qst1(a, l, pivot - 1);
		qst1(a, pivot + 1, r);
	}
}
int Partition2(double a[][3], int l, int r) {//初始化待划分区间
	int i = l, j = r;
	double pivot = a[l][1];//第一个数的Y
	int index = a[l][2];//a[][3]记录按Y排序的点顺序,最左边数的Y顺序
	
	

	while (i < j)
	{		//右侧扫描
		while (i < j && a[j][1] >= pivot)
		{
			j--;//找到小的
		}
		if (i < j)//左小于右
		{
			swap(a[i][2], a[j][2]);
		}

		while (i < j && a[i][1] <= pivot)
		{
			i++;
		}
		if (i < j)
		{
			swap(a[i][2], a[j][2]);
		}
	}
	a[i][2] = index;

	

	return i;
}
//进行快速排序
void qst2(double a[][3], int l, int r) {

	
	if (l < r)
	{
		double pivot = Partition2(a, l, r);
		qst2(a, l, pivot - 1);
		qst2(a, pivot + 1, r);
	}
	//cout << "in qst2" << endl;
	//for (int i = l; i <= r; i++) {
	//	cout << "a[" << i << "][2]:" << a[i][2] << endl;
	//}
}
double smaller(double a,double b) {
	return a<b ? a : b;
}
double getdist(double a[], double b[]) {
	double ans = sqrt(pow(a[0] - b[0], 2) + pow(a[1] - b[1], 2));
	return ans;
}
double MinDistance(double a[][3],int l ,int r ) {
	int n = r+1 - l;//右+1-左
	int m = (l+r+1)/2;
	double distance = 0.0;//
	 if (n == 2) {//两个点
		distance = getdist(a[0], a[1]);
	}
	else if (n == 3) {//三个点
		double d1= getdist(a[0], a[1]);//点0,1
		double d2= getdist(a[2], a[1]);//点2,1
		double d3= getdist(a[0], a[2]);//点0,2
		double d4 = smaller(d1, d2);
		distance = smaller(d4, d3);
	}
	else { //划分
		int lb=m,rb=m;
		double e1 = MinDistance(a,l,m );
		double e2 = MinDistance(a,m,r);
		distance = smaller(e1, e2);
		while (lb>=l && abs(a[lb][0]-a[m][0])<distance) { //从中垂线向两边找区间
			lb--;//从m--
		}
		while (rb<=r && abs(a[rb][0] - a[m][0]) < distance) {
			rb++;//从m++
		}
		lb += 1;
		rb -= 1;

		int inde = 0;
		int* b = new int[rb - m + 1];
		for (int i = 0; i < n; i++) {
			if (a[i][2] >= m && a[i][2] <= rb) {
				b[inde] = a[i][2];//b有序存放中线右边的y
				inde++;
			}
		}
		for (int i = lb; i < m; i++) {// 遍历中线左边的点
			int lk = 0;//lk:y的排序 0-rb - lb + 1
			while (abs(a[b[lk]][1] - a[i][1]) > distance && lk < rb - m + 1) {
				lk++;
			}
			int t = 0;
			while (lk < rb - m + 1 && t < 6) {
				distance = smaller(getdist(a[i], a[b[lk]]), distance);
				lk++;
				t++;
			}
		
		}
		
		delete[] b;
		//for (int i = lb; i < m; i++) {// 遍历跨越中线的点
			
			//for (int k = m;k<=rb ; k++) {//从右边的点里找Y距离小于distance  && ny<6
				//if (  abs(a[k][1] - a[i][1]) < distance) {
					
					//distance = smaller(getdist(a[i], a[k]), distance);
					
				//}
				
			//}
			
			
			
		//}
	}
	return distance;
}
int main() {     //
	int n;//元素个数
	double d = 0.0;
	double a[100000][3] = { 0 };//a[][0]x,a[][1]是y
	cin >> n;
	for (int i = 0; i < n; i++) {//输入
		scanf("%lf %lf",&a[i][0],&a[i][1]);
		a[i][2] = i;
	}
	
	qst1(a, 0, n - 1);// !!!!!!!!!!!!!!!!!!!!!!!!!!!!n-1
	qst2(a, 0, n - 1);

	//for (int i = 0; i < n; i++) {
	//	cout<<"all:" << endl;
	//	cout << "a[" << i << "][0]:" << a[i][0] << " " << "a[" << i << "][1]:" << a[i][1] << " a[" << i << "][2]:" << a[i][2] <<endl;
	//}
	//cout << endl;///

	d=MinDistance(a, 0, n-1);
	printf("%.2f\n", d);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值