最近点对
对平面上给定的N个点,给出所有点对的最短距离,即,输入是平面上的N个点,输出是N点中具有最短距离的两点
思路
暴力思路:
使用双重for循环,遍历平面中的所有点对并计算距离,通过比较得到最短距离,时间复杂度为O(N2)
分治法求解:
- 划分:
- 根据坐标x对平面上的所有点进行排序(x相同则比较y),排序便于随后的划分
- 在平面上对所有点不断进行二分,当剩余一个点和两个点时可以直接求解,剩余一个点时,距离为∞,剩余两个点时可以直接计算距离。
- 二分过程中记录左右两部分中的最短距离dist1和dist2
- 求解:首先,最短距离可能存在于左半部分、右半部分和中间部分,其中左半部分和右半部分可以在递归过程中求出,中间部分则还需计算求解,求解方法:取左右两部分的最短距离记为 δ \delta δ,划分一块“中间地带”,距离中位线 的距离为 δ \delta δ,此时对中间地带的所有点按y坐标进行排序,比较6个点即可判断出中间地带是否存在最短距离
一些思考:- 为什么只有在2 δ \delta δ* δ \delta δ区域(黑色方框)才是合理范围:首先中间地带的点对距离要小于 δ \delta δ,即左右两部分的最短距离才是有效的,而方框区域内,沿y轴方向的长度为 δ \delta δ,因此在这个范围内的点,才能小于左右两部分求得的最短距离
- 为什么需要沿y轴排序:根据上一个问题中,沿y轴方向找出最短距离,因此通过排序的贪心策略更容易实现
- 为什么只用遍历当前点之后的6个点:经过当前点,作一个2 δ \delta δ* δ \delta δ的矩形区域,只有位于矩形四个顶点和矩形与中位线交点上两个点才能满足距离小于 δ \delta δ
- 合并:由于找的是最短距离,无需合并操作
递归体:对平面上的点进行划分,求解并比较中间地带的最短距离和左右两部分最短距离的大小,求出该题的最近点对
递归出口:当剩余一个点和两个点时,可直接计算并返回
代码
暴力
//暴力
void exhaustion(){
for(int i = 0; i < n; ++i){
for(int j = i + 1; j < n; ++j){
double temp = getDist(nodes[i], nodes[j]);
if(temp < ans){
ans