在平面上输入一系列的点,找出其中距离最近的两个点,并将其坐标输出。一种比较简单的做法是采用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;
}