参考:https://blog.csdn.net/Zhang_Chen_/article/details/102417129

前言:

  • 首先,什么是凸包?
    假设平面上有p0~p12共13个点,过某些点作一个多边形,使这个多边形能把所有点都“包”起来。当这个多边形是凸多边形的时候,我们就叫它“凸包”。如下图:
  • 然后,什么是凸包问题?
    我们把这些点放在二维坐标系里面,那么每个点都能用 (x,y) 来表示。
    现给出点的数目13,和各个点的坐标。求构成凸包的点?

概述:

  • 凸包(Convex Hull)是一个计算几何(图形学)中的概念。
    在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点( X1 ,. . . ,Xn )的凸组合来构造。
  • 在二维欧几里得空间中,凸包可想象为一条刚好包着所有点的橡皮圈。用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点
  • 凸包问题:给定点集,求构成凸包的点

1. 穷举法(暴力法、蛮力法)

  • 时间复杂度:O(n³)。
  • 思路:两点确定一条直线,如果剩余的其它点都在这条直线的同一侧,则这两个点是凸包上的点,否则就不是。
  • 步骤:
  1. 将点集里面的所有点两两配对,组成
    条直线。对于每条直线,再检查剩余的( n − 2 ) 个点是否在直线的同一侧。

  2. 如何判断一个点p3p3p3是在点p1、p2p1、p2p1、p2连成的直线的左边还是右边呢?坐标:p1(x1,y1),p2(x2,y2),p3(x3,y3)p 1 ( x 1 , y 1 ) , p 2 ( x 2 , y 2 ) , p 3 ( x 3 , y 3 )p1(x1,y1),p2(x2,y2),p3(x3,y3)

2. 分治法

  • 时间复杂度:O(n㏒n)。
  • 思路:应用分治法思想,把一个大问题分成几个结构相同的子问题,把子问题再分成几个更小的子问题……。然后我们就能用递归的方法,分别求这些子问题的解。最后把每个子问题的解“组装”成原来大问题的解。
  • 步骤:
  1. 把所有的点都放在二维坐标系里面。那么横坐标最小和最大的两个点P1、PnP1、PnP1、Pn一定是凸包上的点。直线P1PnP1PnP1Pn把点集分成了两部分,即X轴上面和下面两部分,分别叫做上包和下包。
  2. 对上包:求距离直线P1PnP1PnP1Pn最远的点,假设为点PmaxPmaxPmax。
  3. 作直线P1Pmax、PnPmaxP1Pmax、PnPmaxP1Pmax、PnPmax,把直线P1PmaxP1PmaxP1Pmax左侧的点当成是上包,把直线PnPmaxPnPmaxPnPmax右侧的点也当成是上包。
  4. 重复步骤 2、3。
  5. 对下包也作类似操作。
  6. 然而怎么求距离某直线最远的点呢?设有一个点p3p3p3和直线p1p2p1p2p1p2。坐标:p1(x1,y1),p2(x2,y2),p3(x3,y3)p1(x 1 , y 1 ) , p 2 ( x 2 , y 2 ) , p 3 ( x 3 , y 3 )p1(x1,y1),p2(x2,y2),p3(x3,y3)

    注意:在步骤1中,如果横坐标最小的点不止一个,那么这几个点都是凸包上的点,此时上包和下包的划分就有点不同了,需要注意。

代码实现:

#include <iostream>
#include <vector>using namespace std;vector<vector<int>> convex_hull; /*convex_hull储存所有凸包点*//*GetResult()实现功能:以坐标P0(x1,y1)和Pn(x2,y2)为直线,找出pack里面里这条直线最远的点Pmax
并找出直线P0Pmax和PmaxPn的上包,进行递归
*/void GetResult(vector<vector<int>> point, int x1, int y1, int x2, int y2)
{/*tmax:最远点在point中的索引Rmax:最远距离的值*/int i, x3, y3, R, Rmax, tmax; vector<vector<int>> result_pack; /*存放上包点或者下包点*//*上包点或者下包点计数,初始化为零*/result_pack.push_back({0});x3 = point[1][0];y3 = point[1][1];R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;Rmax = R;tmax = 1;if (R >= 0){result_pack.push_back({x3, y3});result_pack[0][0] = result_pack[0][0] + 1;}for(int i=2;i<=point[0][0];i++) /*从点集的第二个点开始循环*/{x3 = point[i][0];y3 = point[i][1];R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;if(R >= 0) /*如果R>=0,则是同一测包(上包或下包)的点*/{result_pack.push_back({x3, y3});result_pack[0][0] = result_pack[0][0] + 1;}if(R > Rmax){Rmax = R;tmax = i;}} /*找到一测距离直线最远的点的距离和索引*/if(Rmax <= 0) /*如果已经是边界点了*/{for(int i=1;i<=result_pack[0][0];i++){x3 = result_pack[i][0];y3 = result_pack[i][1];R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;if(R == 0 && !((x3==x2&&y3==y2)||(x3==x1&&y3==y1))) /*如果R是零并且这个新点不是决定直线的两个点,则加入凸包点集合*/{convex_hull.push_back({result_pack[i][0], result_pack[i][1]});convex_hull[0][0] = convex_hull[0][0] + 1;}}return;}else{convex_hull.push_back({point[tmax][0], point[tmax][1]});convex_hull[0][0] = convex_hull[0][0] + 1;if(result_pack[0][0] == 0)return;}GetResult(result_pack, x1, y1, point[tmax][0], point[tmax][1]);GetResult(result_pack, point[tmax][0], point[tmax][1], x2, y2);
}int main(int argc, char** argv)
{vector<vector<int>> pointset; /*pointset储存所有点*/int count=1; /*整型变量conut用于计数*/int x1, y1, x2, y2, x3, y3; /*三个点的坐标*/convex_hull.push_back({0}); /*convex_hull的第一行第一列元素存放凸包点的个数,初始化为0*/pointset.push_back({0}); /*pointset的第一行第一列元素存放点集里面有几个点,初始化为0*/cout<<"===请输入所有点的坐标==="<<endl;/*初始化点集*/int x, y;while(count<20) /*设置输入20个点*/{cout<<"请输入点"<<count<<"的x轴坐标:"<<endl;cin>>x;cout<<"请输入点"<<count<<"的y轴坐标:"<<endl;cin>>y;pointset.push_back({x, y});count++;}/*点集里一共有多少个点*/pointset[0][0] = count-1; x1 = pointset[1][0];y1 = pointset[1][1];x2 = x1;y2 = y1;for(int i=2;i<=pointset[0][0];i++){x3 = pointset[i][0];y3 = pointset[i][1];if(x3 < x1){x1 = x3;y1 = y3;} /*找到x最小的点赋给(x1, y1)*/else if(x3 > x2){x2 = x3;y2 = y3;} /*找到x最大的点赋给(x2, y2)*/}/*两点是凸包点*/convex_hull.push_back({x1, y1});convex_hull.push_back({x2, y2});/*凸包点个数加二*/convex_hull[0][0] += 2;/*因为新x1-x2和x2-x1符号相反,所以上包点和下包点对应的“计算距离公式分子绝对值内的数学表达式”的一正一负所以下面调换x1和x2顺序作为输入保证两者计算的“计算距离公式分子绝对值内的数学表达式”为正的情况各是上包点和下包点中的一种*/GetResult(pointset, x1, y1, x2, y2);GetResult(pointset, x2, y2, x1, y1);/*打印凸包点*/cout<<"\n\n构成凸包的点有:"<<endl;for(int i=1;i<=convex_hull[0][0];i++){cout<<"("<<convex_hull[i][0]<<", "<<convex_hull[i][1]<<")"<<endl;}
}

3. Jarvis步进法

  • 时间复杂度:O(nH)。(其中 n 是点的总个数,H 是凸包上的点的个数)
  • 思路:纵坐标最小的那个点一定是凸包上的点,例如下图中的P0P 0P0。从P0P 0P0开始,按逆时针的方向,逐个找凸包上的点,每前进一步找到一个点,所以叫作步进法。
  • 怎么找下一个点呢?利用夹角。假设现在已经找到P0,P1,P2P 0 , P 1 , P 2P0,P1,P2了,要找下一个点:剩下的点分别和P2P 2P2组成向量,设这个向量与向量P1P2P 1 P 2P1P2的夹角为 β 。当 β 最小时就是所要求的下一个点了,此处为P3P 3P3。
  • 注意
  1. 找第二个点P1P 1P1时,因为已经找到的只有P0P 0P0一个点,所以向量只能和水平线作夹角 α,当 α 最小时求得第二个点。
  2. 共线情况:如果直线P2P3P 2 P 3P2P3上还有一个点P4P 4P4,即三个点共线,此时由向量P2P3P 2 P 3P2P3和向量P2P4P 2 P 4P2P4产生的两个 β 是相同的。我们应该把P3、P4P 3、 P 4P3、P4都当做凸包上的点,并且把距离P2P 2P2最远的那个点(即上图中的P4P 4P4)作为最后搜索到的点,继续找它的下一个连接点。

4. Graham(格拉翰)扫描法

  • 时间复杂度:O(n㏒n) 。
  • 思路:Graham扫描的思想和Jarvis步进法类似,也是先找到凸包上的一个点,然后从那个点开始按逆时针方向逐个找凸包上的点,但它不是利用夹角。
  • 步骤:
  1. 把所有点放在二维坐标系中,则纵坐标最小的点一定是凸包上的点,记为P0P 0P0。
  2. 把所有点的坐标平移一下,使P0P 0P0作为原点。
  3. 计算各个点相对于P0P 0P0的幅角 α ,按从小到大的顺序对各个点排序。当 α 相同时,距离P0P 0P0比较近的排在前面。例如上图得到的结果为P1,P2,P3,P4,P5,P6,P7,P8P 1 , P 2 , P 3 , P 4 , P 5 , P 6 , P 7 , P 8P1,P2,P3,P4,P5,P6,P7,P8。我们由几何知识可以知道,结果中第一个点P1P 1P1和最后一个点P8P 8P8一定是凸包上的点。
  4. 以上,我们已经知道了凸包上的第一个点P0P 0P0和第二个点P1P 1P1,我们把它们放在栈里面。现在从步骤3求得的那个结果里,把P1P 1P1后面的那个点拿出来做当前点,即P2P 2P2。接下来开始找第三个点。
  5. 连接栈最上面的两个元素,得到直线L。看当前点是在直线L的右边还是左边。如果在直线的右边就执行步骤6;如果在直线上,或者在直线的左边就执行步骤7。
  6. 如果在右边,则栈顶的那个元素不是凸包上的点,把栈顶元素出栈。执行步骤5。
  7. 当前点是凸包上的点,把它压入栈,执行步骤8。
  8. 检查当前的点是不是步骤3那个结果的最后一个元素。是最后一个元素的话就结束。如果不是的话就把当前点后面那个点做当前点,返回步骤5。
  9. 最后,栈中的元素就是凸包上的点了。
    以下为用Graham扫描法动态求解的过程:
  • Graham(格拉翰)扫描法要求开始时必须至少知道一个必然在凸包上的点作为起始点(还好这比较简单)。它有个缺点就是直接用它去求一个给定多边形的凸包可能会导致错误,因此算法开始前必须将点有序化。

5. Melkman算法

  • 时间复杂度:O(n)
  • 这个算法是基于Graham扫描线算法的,他们的大体思路相同,但是不同之处在于,Graham使用一个栈来维护凸包,而Melkman使用一个双头队列来维护凸包,每次不仅仅维护队列头的凸性,也维护队列尾的凸性,因此,它得到每时每刻都包含当前考虑过的所有的点的凸包。

    本人尚未理解…大家可参考以下博客学习:
    https://www.cnblogs.com/dream-it-possible/p/8514706.html
    http://blog.sina.com.cn/s/blog_6937385601013mye.html
    https://blog.csdn.net/qq_37493070/article/details/81774876
    https://blog.csdn.net/thewalker88/article/details/79504704

凸包(Convex Hull)问题算法详解相关推荐

  1. 计算机图形几何算法详解勘误

    一直在看<计算机图形几何算法详解>这本书,但是在用的过程中发现了一些错误,一直以为是自己的错误,后来在网上找到了这本书的勘误信息,不过是英文原版的,但是还是想贴出来,以便查找 07 Jul ...

  2. Matlab人脸检测算法详解

    这是一个Matlab人脸检测算法详解 前言 人脸检测结果 算法详解 源代码解析 所调用函数解析 bwlabel(BW,n) regionprops rectangle 总结 前言 目前主流的人脸检测与 ...

  3. 图论-最短路Dijkstra算法详解超详 有图解

    整体来看dij就是从起点开始扩散致整个图的过程,为什么说他稳定呢,是因为他每次迭代,都能得到至少一个结点的最短路.(不像SPFA,玄学复杂度) 但是他的缺点就是不能处理带负权值的边,和代码量稍稍复杂. ...

  4. C++中的STL算法详解

    1.STL算法详解 STL提供能在各种容器中通用的算法(大约有70种),如插入.删除.查找.排序等.算法就是函数模板,算法通过迭代器来操纵容器中的元素.许多算法操作的是容器上的一个区间(也可以是整个容 ...

  5. 粒子群(pso)算法详解matlab代码,粒子群(pso)算法详解matlab代码

    粒子群(pso)算法详解matlab代码 (1)---- 一.粒子群算法的历史 粒子群算法源于复杂适应系统(Complex Adaptive System,CAS).CAS理论于1994年正式提出,C ...

  6. 基础排序算法详解与优化

    文章图片存储在GitHub,网速不佳的朋友,请看<基础排序算法详解与优化> 或者 来我的技术小站 godbmw.com 1. 谈谈基础排序 常见的基础排序有选择排序.冒泡排序和插入排序.众 ...

  7. 目标检测 RCNN算法详解

    原文:http://blog.csdn.net/shenxiaolu1984/article/details/51066975 [目标检测]RCNN算法详解 Girshick, Ross, et al ...

  8. Twitter-Snowflake,64位自增ID算法详解

    Twitter-Snowflake,64位自增ID算法详解 from: http://www.lanindex.com/twitter-snowflake%EF%BC%8C64%E4%BD%8D%E8 ...

  9. 数据结构与算法详解目录

    数据结构与算法详解是一本以实例和实践为主的图书,主要是经典的数据结构与常见算法案例,来自历年考研.软考等考题,有算法思路和完整的代码,最后提供了C语言调试技术的方法. 后续配套微课视频. 第0章  基 ...

  10. [搜索]波特词干(Porter Streamming)提取算法详解(2)

     接[搜索]波特词干(Porter Streamming)提取算法详解(1), http://blog.csdn.net/zhanghaiyang9999/article/details/4162 ...

最新文章

  1. 敏捷开发:软件与文档
  2. 数字取整或保留小数四舍五入的正确写法
  3. 1151压力变送器型号_罗斯蒙特变送器的标准精度!
  4. 绝对编码和增量编码_用户体验设计师应该学习编码吗? 绝对
  5. hibernate h2变mysql_struts2-hibernate-mysql开发案例 -解道Jdon
  6. Exynos4412 内核移植(四)—— MMU 相关知识解析
  7. django 1.8 官方文档翻译: 2-6-2 遗留的数据库
  8. Zookeeper的Leader选举
  9. split添加limit参数
  10. 截止11月5日,30日内累计跌幅最大的200只股票
  11. 《MySQL必知必会》学习笔记——第三章(了解数据库和表)
  12. 使用DevExpress的PdfViewer实现PDF打开、预览、另存为、打印(附源码下载)
  13. MySQL中用给出的生日查询年龄的方法
  14. 在北京租房 舒舍的租客素质怎么样?
  15. 用Excel快速新建空白文件夹
  16. [转]爬虫的现状和反爬虫
  17. 【CSS】文字超过三行显示省略号...
  18. 直角坐标系与极坐标系了解与转换
  19. Unity Render Streaming 云渲染
  20. 【店小蜜】欢迎语卡片的设置和测试

热门文章

  1. POJ 3626 Mud Puddles (BFS)
  2. 【Java从零到架构师第③季】【41】SpringBoot-配置文件_YAML_lombok_设置Banner
  3. 浅谈韦达定理的“来龙去脉”
  4. SQL Server 2000 数据库安装与配置图文教程
  5. 微信小程序获取收货地址流程
  6. [蓝桥杯解题报告]第九届蓝桥杯大赛省赛2018(软件类)真题C++A组 Apare_xzc
  7. nodejs之utility,crypto使用
  8. Pyhton计算一元二次方程的根
  9. 微信小程序自定义地址组件
  10. 语音识别(ASR)论文优选:粤语语料集Automatic Speech Recognition Datasets in Cantonese Language: A Survey and a New D