这篇文章将会简单介绍一下计算凸包的两种算法:Gift Wrapping Algorithm 和 Andrew's Algorithm
首先说一下什么是凸包,,用最通俗的话来讲,在一个二维坐标平面上,随便给你一些点,然后让你从这些点里选择一部分连起来,要求连起来形成的这个图形是凸多边形,并且能够把给出的所有点包起来,并且选择的这些点的数目最少。
为了辅助说明,在这里举一个例子。
建立一个平面直角坐标系,假设给出的点坐标是(1,1),(3,2),(-4,0),(0,0),(2,2),(3,-1),做出的凸包就应该是这个样子的。点(0,0)和(1,1)被包在了内部,而剩下的那些点。共同构成了包围所有点的
在了解了凸包的意思之后,就开始讲两种算法。
第一种算法:(Gift Wrapping Algorithm)礼物包装算法
礼物包装算法的大致流程是这样的:首先取一个起始点,确定一个基准方向(一般设定为y轴正方向),然后以这个起始点为参照考察剩余的每一个点,选取其中连线与基准方向夹角最小的点,将这个点视作起点,然后再重复进行这样的操作,直到形成一个闭合的多边形。这样的多边形就是凸包,而多边形的几个顶点就是我们zz所需的顶点。
这种算法会对全部顶点进行多边形顶点数次数的考察,因此时间复杂度为O(nv),其中n为给出的全部顶点数,v为多边形的顶点数
第二种算法:(Andrew's Algorithm)安德鲁算法
与礼物包装算法不同,安德烈算法的时间复杂度仅为(n)
安德鲁算法在大体上与礼物包装算法相同,但是时间复杂度要优于前者,这是由于安德鲁算法在进行连接多边形的边这个操作之前,对每个顶点编了一个号。其中一种编号对策如下,按照点的横坐标值排序,横坐标值越小,点的序号越小。而在有数个点横坐标值相同的情况下,纵坐标值较小的点会获得较小的编号。
而在多边形连接边的部分,安德鲁算法会从编号为1的顶点开始,按编号从大到小顺序对每一个顶点进行考察。如果这个点在目前已有连线的右边,就将这个点连起来,加入多边形的集合;而假如这个点在目前已有连线的左边,则要按照连线的先后顺序依次切断连起来的边,直至这个点在连线的右边,或者根本没有连线。下面我将会举一个例子,分步骤地叙述这一部分。
首先,我们选取编号为1的顶点,连接b编号为2的,之后考察3号顶点。3号顶点在1与2连线的左边,应该切断1与2连接的边,然后将1与3连接。
之后以3为基准点考察点4,点4在1与3连线的右侧,所以将3与4连接。
考察点5,点5在3与4连线的左侧,因此应该断开3与4的连线,转而将3与5连接。
考察点6,点6在3与5连线右侧,所以连接5与6.
考察点7,点7在5与6连线左侧,所以断开5与6,然而,点7也在3与5连线的左侧,因此应该断开3与5,连接3与7。
考察点8,点8在3与7连线右侧,所以连接点7和点8。
这是我们注意到,8号已经是最大的标号了。因此我们要从8开始,按标号从大到小的顺序再考察一遍那些还不在多边形上的点。首先从6号开始,顶点6在7与8连线右侧,因此连接8与6。
考察点5,点5在8与6连线右侧,连接6与5。考察点4,点4在5与6连线左侧,因此断掉5与6的连线,连接4与6.考察点2,点2在4与6连线右侧,连接2与4。
最后考察点1,点1在2与4连线左侧,因此切断2与4,连接1与4。
至此,凸包计算完成。
礼物包装算法与安德鲁算法的实现(C与java)
这两种算法共同需要的,是一个能存储这些点的空间。其中前者不需要对每个顶点进行标号,而后者需要。并且两个算法都需要一个表示顶点是否已经在多边形上的数据结构。在java中,这个数据结构可以用集合Set来充当。但在没有set结构的C语言中,只能用结构体或者列表的标志位来表示这个点是否已经在多变形中了。而且,安德鲁算法需要一个栈结构,这一结构在C语言中似乎只能用链表或者数组临时充当。
需要注意的一点是,在前者的算法中,由于不知道何时会完成整个凸包的封闭,凸包的起始点要始终保持“不在多边形顶点的集合中”的状态。即在除第一次外的每次遍历中都要考虑起始顶点。而安德烈算法则不用考虑这些。