【学习笔记:计算几何基础2】 Convex Hull

Ahead

10.6.2018
开始第二个算法了
篇章1
前面就不多写了第一篇里面的有些代码后面还用到不重写了

Beginning

算法2 (EE)

概念

极边(Extremity Edge): 也就是两边为相邻极点的边(相邻很重要!!! ) (EE)
非极边 (Non-Extremity Edge):剩下的

分析

和之前的一样
对于每一条极边,也有这样的特性,所有的点都落在同侧同样也是充要的。
这个证明和之前的差不多,想一下也没什么问题的(严谨的我也不会)
所以问题就变成了找极边

伪代码
   枚举边
   枚举点
   如果所有点落在边的一侧 
   它是EE 

可以看出的是时间复杂度降为O(n^3)

代码

void checkEdge(Point s[],int n,int p,int q) 
{
    bool LEmpty = true , REmpty = true; 
    for(int i=1;i<=n && (LEmpty || REmpty);++i) 
    { 
        if(i!=p && i!=q) 
            ToLeft(S[p],S[q],S[k])? LEmpty=false : REmpty = false; 
    } 
    if(LEmpty || REmpty) 
      S[p].extreme = S[q].extreme = true; 
}

void markEE (Point S[],int n) 
{ 
    for(int i=1;i<=n;++i) S[i].extreme = false; 
    for(int i=1;i<=n;++i) 
       for(int j=1;j<=n;++j)
         if(i!=j) 
          checkEdge(S,n,i,j); 
}

算法3 (IC)

前面两个算法还是很慢的
于是又开始又花了
参考插入排序,出现了这样一个算法
如果对于一个已经完成的凸包,会有这样几种情况
1502976-20181006085452690-1413043349.png
所以对于新出现的点,如果它在里面,直接忽略,如果在外面,那么这个点一定是当前的合法EP,同时对原来的进行凸包进行适当调整

判定

但是,我们首要的任务是判定是否在凸包内= =
于是我们引入一个测试 In-Convex-Polygon Text
对于找点,似乎我们可以二分查找!!!!(这里就直接引视频中的图了)
1502976-20181006090356853-2062450014.png
对于凸包上的点排序,每次找到mid点,连接原点与mid,然后ToLeft一次判定点在那边,然后缩小范围,最后形成最后一个三角形是,就可以判断在内还是在外了
那么时间复杂度似乎到了nlogn??
但是令人失望的是,似乎并不行,类比插排,插排的二分查找缺陷在于动态变换的区间加点不容易 ,如果用vector的话上界也会到n,而凸包面临的也是如此
所以我们就直接暴力吧,和插排一样。那么如何暴力?
对于前面两种算法,可以再找左边? ? 
所以,我们就直接CCW方向遍历边,然后做ToLeft 这个点在所有边的左侧,那么,显然在内部,否则在外部。 那么测试时间为n
总时间复杂度降为O(n^2);

添加

那么判定完后,要干的事就是添加了,一个很直观的想法,类似于黑夜中开灯
1502976-20181006092236428-488731770.png
虚线部分就是要删除的实线形成了新的凸包。
可以发现的是,这似乎是两个临界的s和t向x连边拓展,去掉ts,保留st
所以现在的任务就是寻找s和t
这里的s和t称之为切线(Tangent)或者支持线(Suppor-Lines)

寻找

那么继续观察,除去s,t 剩下的点v与x的连线,对于v的前驱和后记,必然属于两个模式(pattern)即一个在左一个在右
那么出现以下情况

  • 如果状态是LL 那么是s
  • 如果状态是RR 那么是t
  • 如果状态是RL 那么它位于st上,是保留点
  • 如果状态是LR 那么它位于ts上,是删除点
    而对于LR的判定只需要两次ToLeft
    更近一步我们可以发现In-Convex-Polygon Text 似乎也可以那么处理
    1502976-20181006093603843-2052024487.png
    如果在内部,则不存在t与s

    伪代码

    遍历凸包上的点v
    记录ToLeft(x,v,pre[v]) 与ToLeft(x,v,nxt[v])
    如果状态是LL 那么是s
    如果状态是RR 那么是t 
    如果状态是RL 那么它位于st上
    如果状态是LR 那么它位于ts上 
    如果出现过s与t扩展删除
    否则 返回 

    代码

void InConvexPolygon Text(Point S[],Point x) 
{ 
    bool flag=0; 
    for(int i=1;i<=n;++i)  
      if(S[i].extreme) 
    {
        bool P[i] = ToLeft(x,i,S.pre[i]); 
        bool N[i] = ToLeft(x , i ,   S.nxt[i]); 
        if(P[i]==N[i]) flag=1; 
    }  
   if(!flag) return ; 
   else 
    { 
        S[x].extreme=1; 
       for(int i=1;i<=n;++i)  
          if(S[i].extreme) 
        {
            if(P[i]==0 && N[i]==0) S[i].nxt=x,S[x].pre=i;  
            else if(P[i]==1 && N[i]==0) S[i].extreme = 0; 
            else if(P[i]==1 && N[i]==1) S[i].pre=x,S[x].nxt=i;  
        }  
    }
}

于是就形成了O(n^2)的算法

算法4(GW)

这个算法时间复杂度为O(n^2) 但是在绝大多数情况下达不到,最优化到达O(n);
对比选择排序和EE算法
我们可否减少每次查找边的范围 ?
答案是显然的
首先,构成的凸包肯定是环状的结构。那么,对于我们当前的还未完成的凸包,一定是从两个端点出发寻找下一条极边 直到形成环路
1502976-20181006100639429-1408162375.png

拓展

1502976-20181006101008099-119177106.png
那么对于下一个点s有什么特点? 再次对比前面---ToLeft
所以,ks 一定满足所有点位于L
那么算法就很明显的 对于当前的端点,枚举所有点做ToLeft更新
然后将k更新为s
那么第一个点怎么找,方便起见,引入一个概念
lowest-then-leftmost 点(LTL点) 即最下最左点
以y坐标为第一关键字最小,x坐标为第二关键字最小
对于第二个极点选排名第二的LTL

伪代码

   找到LTL  
    i=LTL; 
    找出第一条的EE
    循环 
        遍历点 
         如果新点在选定点的右边,更新
        i更新为k k更新为新点 
    直到形成环 

时间复杂度上,显然遍历次数是环的大小,所以是O(nh),h为环的大小
那么当h只有3时,结果就到了O(n) O(答案?? )【据说这叫Output Sensitive】
那么这章也就先到这里了 Gw的代码网上很多不想写了,有时间补上去吧

转载于:https://www.cnblogs.com/PiCaHor/p/9746557.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值