(学习5 分治思想)最短对的问题

Posted pipihoudewo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(学习5 分治思想)最短对的问题相关的知识,希望对你有一定的参考价值。

最短对问题:

 在一个拥有n个点的空间中,求其中欧几里得距离最小的点对。

 

解析

原理理解的参考视频:https://www.bilibili.com/video/BV1Y7411w71e?from=search&seid=14190590970009131408

1:点的数量较少的情况可以直接用蛮力算法计算出,比如 n=1或者2;

2:点的数量较多的情况,就需要利用分治的思想来实现,首先将每个点按照X轴升序排序,将点集合Q一分为二,称为Q1,Q2。然后分别求出Q1、Q2中最短点对d1与d2;但是我们不能断定求出的d=min(d1,d2)就是这个空间里最短点对,因为在Q1、Q2分割线两侧可能有两点之间的距离比d1和d2还小。所以我们设分隔线与x=m,找出Q点集中[m-d,m+d]之间的点,记该点集为Q3,然后将Q3中的点以y轴按升序排序,按照视频里的讲解,最多只用查找7个点,这是因为每个落在区域[m-d,m+d]的点,其对面的点一定落在一个边长为d的正方形边上,因为任何其他落在这个正方形以外的点的长度一定大于这个正方形边上的点,也不可能落在正方形里面,不然就违背了d最小的前提,所以更新d的操作的时间复杂度是比较低的,然后遍历找是否出现两点之间的距离少于d的,如果比d还短则更新d,最后输出的d就是该平面最小的距离。

代码

//
//  main.cpp
//  作业5
//
//  Created by yizhihenpidehou on 2020/3/24.
//  Copyright © 2020 yizhihenpidehou. All rights reserved.
//
/*问题:求二维空间中欧几里得距离最小的点对*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxen=50000;
struct Node{  //存储节点
    double x;
    double y;
};
bool cmpX(Node a,Node b){ //对X轴排序
    return a.x<b.x;
}
bool cmpY(Node a,Node b){ //对Y轴排序
    return a.y<b.y;
}
double dist(Node a,Node b){ //返回距离
    return sqrt(pow(a.x-b.x,2)+pow(a.y-b.y,2));
}
Node arr[maxen];
double dirctSearch(int n,Node a[]){
    if(n==1) return 0;
    else return dist(a[1],arr[2]);
}
double EfficientClosestPair(int l,int r){
    int mid=(l+r)/2;
    int count=1;
    double d=0;
    if(l==r) return 999999;
    d=(min(EfficientClosestPair(l,mid),EfficientClosestPair(mid+1,r)));
    Node tmp[maxen];
    for(int i=mid;i>=l&&(arr[mid].x-arr[i].x<d);i--){  //筛选出左边间距小于d的
        tmp[count++]=arr[i];
    }
    for(int i=mid+1;i<=r&&(arr[i].x-arr[mid].x<d);i++){ //筛选出z右边间距d小于d的
        tmp[count++]=arr[i];
    }
    sort(tmp+1,tmp+count,cmpY); //将按d划分的区间中的点集合按照y轴排序
    for(int i=1;i<count;i++){
        for(int j=i+1;j<count&&(arr[j].y-arr[i].y<d);j++){
            d=min(d,dist(arr[i],arr[j]));//更新d
        }
    }
    return d;
}
int main(int argc, const char * argv[]) {
    int n;
    double minDist;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lf %lf",&arr[i].x,&arr[i].y);
    }
    if(n<=2){
        minDist=dirctSearch(n, arr); //若点的数量比较少,则直接暴力
    }
    else{   //采用分治思想,二分找出两个区间最小的点对距离,然后再找出按照最小点对划分的区间的中间区域查找是否有更短的点对
        sort(arr+1,arr+1+n,cmpX); //按照X轴排序
        minDist=EfficientClosestPair(1,n);
    }
 //   printf("%.2f
",sqrt(10));
    printf("%.2f
",minDist);
    return 0;
}

 

时间复杂度分析:最好的情况:o(logn),划分后区域Q1、Q2上的点比较稀疏

                        最坏的情况:o(n²),有可能所有点都在[m-d,m+d]上,这样第三次更新d就比较费时了

github地址:https://github.com/yizhihenpidehou/bananas/tree/master/%E7%AC%AC%E4%BA%94%E5%91%A8 

以上是关于(学习5 分治思想)最短对的问题的主要内容,如果未能解决你的问题,请参考以下文章

算法学习——利用归并排序求逆序对的数量

LeetCode Array Easy 53. Maximum Subarray 个人解法 和分治思想的学习

逆序对的两种求法

「五大常用算法」一文图解分治算法和思想

算法学习---分治法和快速排序

算法学习 :分而治之思想