寻找最近点对

给定平面上N个点的坐标,找出距离最近的两个点。

根据水平方向的坐标把平面上的N个点分成两部分Left和Right。假设分别求出了Left和Right两部分中距离最近的点对之最短距离MinDist(Left)和MinDist(Right),还有一种情况没有考虑,那就是点对中一个点来自于Left部分,另一个点来自Right部分。最直接的想法,那就是穷举Left和Right两个部分之间的点对,这样的点对很多,最多可能有N*N/4对。是否可以只考虑有可能成为最近点对的候选点呢?由于我们已经知道Left和Right两个部分中的最近点对距离分别为MinDist(Left)和MinDist(Right),如果Left和Right之间的点对距离超过MDist=min(MinDist(Left),MinDist(Right)),我们则不会对他们感兴趣,因为这些点对不可能是最近点对。

通过直线x=M将所有的点对分成x<M和x>M两部分(按照横坐标划分),只要考虑x=M-MDist到x=M+MDist之间的这个带状区域内的最小点对,然后再更MDist比较就可以了。在计算带状区域的最小点对时,可以按Y坐标,对带状区域内的顶点进行排序,如果一个点对的距离小于MDist,那么他们一定在一个MDist*(2*MDist)的矩形区域内。一个MDist*(2*MDist)的区域内最多有8个点,对于任意一个带状区域内的顶点,只要考察它与按照Y坐标排序且紧接着的7个点之间的距离就可以了。根据这个特点,我们可以用O(N)时间完成带状区域最近点对的查找。

以下代码:简化了查找过程,逐一计算带状区域内点对的距离。

#include <stdio.h>  

#include <algorithm>  
#include <vector>  
#include <math.h>  
class Point {
public:
Point(int x, int y) : x_(x), y_(y) {}
Point() : x_(0), y_(0) {}
static bool OrderByX(const Point& left, const Point& right) {
return left.x_ < right.x_;
}
static bool OrderByY(const Point& left, const Point& right) {
return left.y_ < right.y_;
}
int x_;
int y_;
};
float Distance(const Point& left, const Point& right) {
return sqrt(pow(left.x_ - right.x_, 2) + pow(left.y_ - right.y_, 2));
}
int NearestPoints(const std::vector<Point>& points, int start, int end, Point* point1, Point* point2) {
if (end > start) {
int middle = (start + end) / 2;
int left_min_distance = NearestPoints(points, start, middle, point1, point2);
int right_min_distance = NearestPoints(points, middle + 1, end, point1, point2);
int min_distance = left_min_distance > right_min_distance ? right_min_distance : left_min_distance;
std::vector<Point> left_part_points;
for (int i = start; i <= middle; ++i) {
if (points[middle].x_ - points[i].x_ <= min_distance) {
left_part_points.push_back(points[i]);
}
}
sort(left_part_points.begin(), left_part_points.end(), Point::OrderByY);
std::vector<Point> right_part_points;
for (int i = middle + 1; i <= end; ++i) {
if (points[i].x_ - points[middle].x_ <= min_distance) {
right_part_points.push_back(points[i]);
}
}
sort(right_part_points.begin(), right_part_points.end(), Point::OrderByY);
int distance_y = 0;
int point_distance = 0;
for (int i = 0; i < left_part_points.size(); ++i) {
for (int j = 0; j < right_part_points.size(); ++j) {
distance_y = left_part_points[i].y_ > right_part_points[j].y_ ? left_part_points[i].y_ - right_part_points[j].y_ :
right_part_points[j].y_ - left_part_points[i].y_;
if (distance_y <= min_distance) {
point_distance = Distance(left_part_points[i], right_part_points[j]);
if (point_distance < min_distance) {
min_distance = point_distance;
*point1 = left_part_points[i];
*point2 = right_part_points[j];
}
}
}
}
return min_distance;
}
else {
return INT_MAX;
}
}
int main(int argc, char** argv) {
std::vector<Point> points;
points.push_back(Point(2, 3));
points.push_back(Point(1, 4));
points.push_back(Point(3, 0));
points.push_back(Point(5, 0));
points.push_back(Point(5, 1));
sort(points.begin(), points.end(), Point::OrderByX);
Point point1;
Point point2;
NearestPoints(points, 0, points.size() - 1, &point1, &point2);
printf("Point1: (%d, %d) <--> Point2: (%d, %d)\n", point1.x_, point1.y_, point2.x_, point2.y_);
system("pause");
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值