分治算法-最近点问题

   在平面上输入一系列的点,找出其中距离最近的两个点,并将其坐标输出。一种比较简单的做法是采用2个for循环计算每2个点之间的距离,找出最短的那个。此方法是傻瓜式的,耗费时间为O(N^2)。采用分治算法:平面上的所有点可以从中间分成2部分,那么最短距离就可能有3中情况,(1)出现在左半平面的2个点距离最近;(2)出现在最右边的平面的2个点的距离最近;(3)两个点,1个在左半平面,1个在右半平面,它们之间的距离最近。在设计程序时,左、右2半平面的最近点距离可以采用递归算法获得,只需要计算跨2个平面最近的距离,然后再比较三种最短距离,选择值最小的。一些过程在程序中说明。

divide.h

#pragma once
#include<iostream>
#include<vector>
using namespace std;
struct P
{
	double x;
	double y;
};
class Dac
{
public:
	void insert(double x,double y);
	void find();
private:
	vector<P> Point;
	vector<double> findmin(int left,int right);
};

divide.cpp

#include "stdafx.h"
#include"divide.h"
#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
void Dac::insert(double x,double y)//将点放在vector中,并按x坐标的大小进行排序
{
	P tem;
	tem.x=x;
	tem.y=y;
	vector<P>::iterator it;
	for(it=Point.begin();it!=Point.end();++it)//将坐标点按x坐标的大小排序
	{
		if(it->x>tem.x)
			break;
	}
	Point.insert(it,tem);
}
void Dac::find()
{
	if(Point.size()<=1)
		cout<<"输入点数应大于2个"<<endl;
	else
	{
	vector<double> a=findmin(0,Point.size()-1);
	cout<<"path:"<<a[0]<<endl;
	cout<<"p1:"<<"("<<a[1]<<","<<a[2]<<")"<<endl;
	cout<<"p2:"<<"("<<a[3]<<","<<a[4]<<")"<<endl;
	}
}
//最小距离出现的三种情况:1.在左半平面;2.在有半平面;3.跨越左右2平面
vector<double> Dac::findmin(int left,int right)//vector中,第一个元素保存的是最小距离,后面的4的元素分别保存最近两点的坐标
{
	    int center;
		if(left+2>=right)//平面上最少为3个点时就蛮力的计算,因为当为3个点不蛮力计算,还进行递归
		{                //则假设R平面有2个点,L平面有一个点,对于L平面一个点无法计算距离
			P p1,p2;//用于保存最近距离的2个点的坐标
			double m;
			m=sqrt((Point[left].x-Point[right].x)*(Point[left].x-Point[right].x)+(Point[left].y-Point[right].y)*(Point[left].y-Point[right].y));
			p1.x=Point[left].x;p1.y=Point[left].y;p2.x=Point[right].x;p2.y=Point[right].y;
	         double tem;
			for(int i=left;i<=right;i++)
				for(int j=i+1;j<=right;j++)
				{
					tem=sqrt((Point[i].x-Point[j].x)*(Point[i].x-Point[j].x)+(Point[i].y-Point[j].y)*(Point[i].y-Point[j].y));
					if(tem<m)
					{
						m=tem;
						p1.x=Point[i].x;
						p1.y=Point[i].y;
						p2.x=Point[j].x;
						p2.y=Point[j].y;
					}
				}
			
			//将短距离和2个点的坐标放在vector中返回
			vector<double>min;
			min.push_back(m);
			min.push_back(p1.x);
			min.push_back(p1.y);
			min.push_back(p2.x);
			min.push_back(p2.y);
			return min;
		}
		else
		{
		center=(left+right)/2;//将平面分成2部分
		vector<double>leftmin=findmin(left,center);//通过递归求得左半平面最近点距离和相应的2个点的坐标
		vector<double>rightmin=findmin(center+1,right);//通过递归求得右半平面最近点距离和相应的2个点的坐标

		vector<double>mpath;//找出左右平面最小距离中的最小的一个
		if(leftmin[0]<rightmin[0])
			mpath=leftmin;
		else
			mpath=rightmin;

	//计算跨中间界线的最近距离
	//先初始化中间的变量
	 P mp1,mp2;
	 double mmin=sqrt((Point[left].x-Point[right].x)*(Point[left].x-Point[right].x)+(Point[left].y-Point[right].y)*(Point[left].y-Point[right].y));
	 mp1.x=Point[left].x;mp1.y=Point[left].y; mp2.x=Point[right].x;  mp2.y=Point[right].y;
	 double tem3;
	 //保留与center位置的x、y坐标之差绝对值小于mpath[0]点,只有这些点其距离才有可能小于mpath[0],将其放在vector中
	 vector<P> p;
	 int c;
	 for(int i=left;i<=center;i++)//寻找左半平面满足要求的点
		 if(((Point[center].x-Point[i].x)<mpath[0])&&(fabs(Point[center].y-Point[i].y)<mpath[0]))
			 p.push_back(Point[i]);
	        c=p.size();//记录p中点是来自左半平面的点
	 for(int i=center+1;i<=right;i++)
		 if(((Point[i].x-Point[center].x)<mpath[0])&&(fabs(Point[center].y-Point[i].y)<mpath[0]))
			 p.push_back(Point[i]);

	 //只计算左半平面的点到右半平面的点
	 for(int i=0;i<=c;i++)//左半平面的点
		 for(int j=c+1;j<=p.size()-1;j++)//右半平面的点
		 {
				 tem3=sqrt((Point[i].x-Point[j].x)*(Point[i].x-Point[j].x)+(Point[i].y-Point[j].y)*(Point[i].y-Point[j].y));
				 if(tem3<mmin)
				 {
					mmin=tem3;
					mp1.x=Point[i].x;
				    mp1.y=Point[i].y;
				    mp2.x=Point[j].x;
				    mp2.y=Point[j].y;
				 }
		 }
		 //比较三种情况的距离最近的,返回最近距离和相应的坐标点
		 if(mmin<mpath[0])
		 {
			 mpath[0]=mmin;
			 mpath[1]=mp1.x;
			 mpath[2]=mp1.y;
			 mpath[3]=mp2.x;
			 mpath[4]=mp2.y;
		 }

		 return mpath;
		}
}


Algorithm-divide1.cpp

//分治算法求解最近距离问题,并输出最近的2个点的坐标
#include "stdafx.h"
#include"divide.h"
#include<iostream>
#include<vector>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
	Dac d;
	d.insert(0,0);
	d.insert(1,1);
	d.insert(-2,-2);
	d.insert(3,5);
	d.insert(3,4);
	d.insert(3,3);
	d.insert(3,3.1);
	d.find();
	return 0;
}



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值