用分治法解最近对问题

本文详细介绍了如何运用分治策略解决寻找平面上点集最近点对的问题。首先将点集划分为两部分,然后递归地求解子问题,最后比较不同情况下的最近点对。核心代码中通过排序和比较来找到跨分割线的最短距离。时间复杂度为O(3nlogn)。
摘要由CSDN通过智能技术生成

用分治法解最近对问题

1.问题

设p1=(x1,y1), p2(x2,y2), …,pn=(xn,yn)是平面上n个点构成的集合S,找出集合S中距离最近的点对。

2.解析

分治法求解:
① 划分

将集合S分成两个大小基本相等的子集S1和S2

② 求解子问题

递归地求解两个子问题

③ 合并问题的解

可能出现三种情况
Ⅰ.组成S的最近点对的2个点都在S1中
Ⅱ.组成S的最近点对的2个点都在S2中
Ⅲ.组成S的最近点对的2个点分别在S1和S2中
比较三种情况下最近点对,取三者之中较小者为原问题的解

3.设计

核心代码实现:
float ClosestPair(Point points[], int length, Point &a, Point &b){  // 求出最近点对记录,并将两点记录再a、b中
    double distance;                   //记录集合points中最近两点距离 
    double d1, d2;                     //记录分割后两个子集中各自最小点对距离
    int i = 0, j = 0, k = 0, x = 0;    //用于控制for循环的循环变量
    Point a1, b1, a2, b2;              //保存分割后两个子集中最小点对

    if (length < 2)
        return INFINITE_DISTANCE;    //若子集长度小于2,定义为最大距离,表示不可达
    else if (length == 2){  //若子集长度等于2,直接返回该两点的距离
        a = points[0];
        b = points[1];
        distance = Distance(points[0], points[1]);
    }
    else{  //子集长度大于3,进行分治求解
        Point *pts1 = new Point[length];     //开辟两个子集
        Point *pts2 = new Point[length];

        sort(points, points + length, compareX);    //调用sort函数对points进行排序,compareX为自定义的排序规则
        double mid = points[(length - 1) / 2].x;    //排完序后的中间下标值,即中位数
        for (i = 0; i < length / 2; i++)
            pts1[i] = points[i];
        for (int j = 0, i = length / 2; i < length; i++)
            pts2[j++] = points[i];

        d1 = ClosestPair(pts1, length / 2, a1, b1);  //分治求解左半部分子集的最近点  
        d2 = ClosestPair(pts2, length - length / 2, a2, b2); //分治求解右半部分子集的最近点  
        if (d1 < d2) { distance = d1; a = a1; b = b1; }  //记录最近点,最近距离
        else { distance = d2; a = a2; b = b2; }
        Point *pts3 = new Point[length];
        for (i = 0, k = 0; i < length; i++)     
            if (abs(points[i].x - mid) <= distance)
                pts3[k++] = points[i];
        sort(pts3, pts3 + k, compareY);      // 以y排序矩形阵内的点集合
        for (i = 0; i < k; i++){
            if (pts3[j].x - mid >= 0)                  // 只判断左侧部分的点
                continue;
            x = 0;
            for (j = i + 1; j <= i + 6 + x && j < k; j++){     //只需与有序的领接的的6个点进行比较
                if (pts3[j].x - mid < 0){    //假如i点是位于mid左边则只需判断在mid右边的j点即可
                    x++;
                    continue;
                }
                if (Distance(pts3[i], pts3[j]) < distance){    //如果跨分割线的两点距离小于已知最小距离,则记录该距离和两点
                    distance = Distance(pts3[i], pts3[j]);
                    a = pts3[i];
                    b = pts3[j];
                }
            }
        }
    }
    return distance;
}

4.分析

时间复杂度:O(3nlogn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值