C++实现使用分治法寻找最近点对

示例输入:
2
3 1 1 2 1 3 5
10 851644 996635 20388 842736 262145 615142 890041 434439 787213 89181 99282 310353 179500 803495 728862 687090 225650 604015 765534 465397

示例输出:
1.00
38153.57

思路及代码:

//
//  main.cpp
//  NearestNodes
//
//  Created by 胡昱 on 2021/10/24.
//

#include <iostream>
#include <cmath>
using namespace std;

// 表示点的结构体
struct Node {
    int x = 0;
    int y = 0;
};


// 根据横坐标从小到大排序,如果横坐标一样则按纵坐标从小到大排序
bool sortByX(Node& p, Node& q) {
    if(p.x == q.x) {
        return p.y < q.y;
    }
    else {
        return p.x < q.x;
    }
}

// 根据纵坐标从小到大排序,如果纵坐标一样则按横坐标从小到大排序
bool sortByY(Node& p, Node& q) {
    if(p.y == q.y) {
        return p.x < q.x;
    }
    else {
        return p.y < q.y;
    }
}

// 计算两个点之间的距离
double getDistance(Node& p, Node& q) {
    return sqrt(1.0 * pow((p.x - q.x), 2) + 1.0 * pow((p.y - q.y), 2));
}

// 使用分治法来寻找最近点对的距离
// 输入为一个已排好序的点数组以及开始下标和结束下标
double getShortestDistance(Node* a, int low, int high) {
    
    double shortestDistance;
    
    int aLength = high - low + 1;
    if(aLength <= 1) {
        shortestDistance = ((long long int)2) << 60;
    }
    // 如果只有两个点则不用进行分治法了
    else if(aLength == 2) {
        shortestDistance = getDistance(a[0], a[1]);
    }
    
    // 如果只剩三个点也不用进行分治法了
    else if(aLength == 3) {
        shortestDistance = min(min(getDistance(a[0], a[1]), getDistance(a[0], a[2])), getDistance(a[1], a[2]));
    }
    
    // 如果大于三个点则需要继续使用分治法
    else {
        
        // 分解成左右两个区域,并得到左右两个区域各自的最近点对距离
        int mid = (low + high) / 2;
        shortestDistance = min(getShortestDistance(a, low, mid), getShortestDistance(a, mid + 1, high));
        
        // 对子问题进行归并,即判断左区域中的点与右区域中的点是否会构成更短的距离
        // 只有横坐标与mid点横坐标的距离小于shortestDistance的点才有可能与另一个子区域的点构成最近点对
        Node* possibleNodes = new Node[aLength];
        int possibleNum = 0;
        for(int i = low; i < high; ++i) {
            if(abs(a[i].x - a[mid].x) < shortestDistance) {
                possibleNodes[possibleNum] = a[i];
                ++possibleNum;
            }
        }
        // 按照纵坐标进行排序以减少比较次数
        sort(possibleNodes, possibleNodes + possibleNum, sortByY);
        // 开始比较可能是跨区域的最近点对之间的距离
        for(int i = 0; i < possibleNum - 1; ++i) {
            for(int j = i + 1; j < possibleNum; ++j) {
                // 如果j点的纵坐标与i点的纵坐标的差距已经超过目前的最短距离,则剩下的点就不用比较了
                if(possibleNodes[j].y - possibleNodes[i].y > shortestDistance) {
                    break;
                }
                else {
                    shortestDistance = min(shortestDistance, getDistance(possibleNodes[i], possibleNodes[j]));
                }
            }
        }
        
        // 释放资源
        delete [] possibleNodes;
    }
    
    return shortestDistance;
}

int main(int argc, const char * argv[]) {
    // 共有m个问题
    int m;
    cin >> m;
    while((m--) > 0) {
        
        // 输入坐标
        int n;
        cin >> n;
        Node* a = new Node[n];
        for(int i = 0; i < n; ++i) {
            cin >> a[i].x >> a[i].y;
        }
        
        // 根据横坐标从小到大排序
        sort(a, a + n, sortByX);
        
        // 寻找最近点对并输出结果
        printf("%.2lf\n", getShortestDistance(a, 0, n - 1));
        
        // 释放资源
        delete [] a;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值