分治算法-求最小点对

A.算法设计原理
1.采用分治的策略,将问题以X的中位数划分为左右俩个子问题分别为minleft=MinDistance(PL,XL,YL),minright= MinDistance(PR,XR,YR)。每个子问题求解得到的值,取较小值d= min{minleft,minright}
2.假设在第一步中找到的最小的距离为d,我们只需要再分别从左右取点p,q,得到距离与d相比较,d=min{d,getDistance(p,q)}。关键在于证明是有限个点。推导过程如下(图像反了)
在这里插入图片描述

B.伪代码描述:

  1. 初始化P,X,Y
  2. 快速排序P(按照x),X,Y,Y在排序前得到对应x得位置MinDistance(P,X,Y)
  3. 若P中点的数目为2,3直接求解
  4. 根据最中间得下标直接划分P,X,根据Y已知对应x的下标拆分X,得到PL,XL,YL,PR,XR,YR;
    求minleft= MinDistance(PL,XL,YL)
    minright= MinDistance(PR,XR,YR)
    d=min(dr,dl)
  5. 在PL距离范围内的每个点,检查PR中是否有点与该点的距离小于d,若有则d取新值。由于上面的拆分其实此时每个子问题Y是有序的,我则从小到大检查,每一个点后继的6个点即可
  6. 返回d
    C.关键问题
    1.拆分点集,关键是让Y中的y知道当前情况下的X的位置,所以需要设计一个特殊的数据结构来记录。
struct Pointxy
{
    int number;
    int myindex;//索引号
    int otherindex;//用于y知道对应得x在X中的位置,也是在P中的位置
};

在X中数据排好序后,y马上记录下来此时x的位置

for (int i = 1; i <= listnumber; i++)
    {
         Y[X[i].myindex].otherindex= i;
    }

由于每次排好序X的x之间的位置是不变的,它只是通过中位线划分了,所以我们只要对Y中记录的x坐标做一个相对变换就好了

void DividXY()
{  
    int mid = n / 2;
    int xl = 0, xr = 0, yl= 0, yr = 0;
    for (int i = 1; i <= n; i++)
    {
         if (i <= mid)
         {
             xl++;
             XL[xl] = X[i];
         }
         else
         {
             xr++;
             XR[xr] = X[i];
         }
    }
    for (int j = 1; j <= n; j++)
    {
         if (Y[j].otherindex <=mid)
         {
             yl++;
             YL[yl] = Y[j];
         }
         else
         {
             yr++;
             YR[yr] = Y[j];
             YR[yr].otherindex -=mid;
         }
    }
}
//划分P
void DividP(Numlist *P, Numlist *PL, Numlist *PR)
{
 int m = 0, n = 0;
 int mid = P->number / 2;
 for (int i = 1; i <= P->number; i++)
 {
  if (i <= mid)
  {
   m++;
   PL->point[m] = P->point[i];
  }
  else
  {
   n++;
   PR->point[n] = P->point[i];
  }
 }
 PL->number = m;
 PR->number = n;
}

关键函数

float MinDistance(Numlist *P, Pointxy X[], Pointxy Y[])
{
    float d;
    int n = P->number;
    if (n == 2)
    {
         d =getDistance(P->point[n], P->point[n - 1]);
    }
    else if (n == 3)
    {
         float d1 = getDistance(P->point[n], P->point[n - 1]);
         float d2 = getDistance(P->point[n], P->point[n - 2]);
         float d3 = getDistance(P->point[n - 1], P->point[n - 2]);
         d = min(d1,min(d2, d3));
    }
    else
    {
         int Mid = n / 2;
         Numlist *PL = new Numlist, *PR = new Numlist;
         Pointxy XL[MAX], XR[MAX];
         Pointxy YL[MAX], YR[MAX];
         DividP(P, PL, PR);
         DiviXY(n, X, Y, XL, YL, XR, YR);
         double minleft =MinDistance(PL, XL, YL);
         double minright =MinDistance(PR, XR, YR)
     	 d =min(minleft, minright);
         //此时Y为有序
         for (int i = 1; i <= n;i++)
         {
             int k = (i + 7) >(n) ? (n) : (i + 7);//从第一个点开始取后面的7个点
             for (int j = i + 1; j <=
k; j++)//只比较往后的6位数
             {
                  int index1 = Y[i].otherindex;//对应P的下标
                  int index2 = Y[j].otherindex;
                  if (Y[j].number - Y[i].number >= d)//大于d立即返回
                      break;
                  else
                  {
                      d= min(d, getDistance(P->point[index1], P->point[index2]));                   
                  }
             }
         }
    }
    return d;
}

对P,X,Y初始化,以及用快速排序排好传参就可以输出最小点对的距离值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值