凸包问题
什么是凸包问题?
- 凸包:包含给定集合X的凸集的交集。
- 概念
-点集Q的凸包(convex hull)是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内。下图中由红色线段表示的多边形就是点集Q={p0,p1,…p12}的凸包。
- 凸包问题:一组平面上的点,求一个包含所有点的最小的凸多边形。
-这可以形象地想成这样:在地上放置一些不可移动的木桩,用一根绳子把他们尽量紧地圈起来,并且为凸边形,这就是凸包了。
常见的求解方法
- Graham扫描法
- Jarvis步进法
分治法
1.Graham扫描法
复杂度
这个算法可以直接在原数据上进行运算,因此空间复杂度为O⑴。但如果将凸包的结果存储到另一数组中,则可能在代码级别进行优化。由于在扫描凸包前要进行排序,因此时间复杂度至少为快速排序的O(nlgn)。后面的扫描过程复杂度为O(n),因此整个算法的复杂度为O(nlgn)。思路:先找到凸包上一个点,然后从那个点按逆时针方向逐个找凸包上的点。
步骤:
- 1.把所有点放在二维坐标系中,找到纵坐标最小的点,这个点一定是凸包上的点,为P0。
- 2.计算各个点与P0连线P0Pi,与水平方向的夹角,按从小到大对各个点进行排序,当夹角相同是,距离P0近的点排在前面,依次为P0,P1,……P(n-1)。由几何知识知道,结果中第一个点和最后一个点一定是凸包上的点。
- 3.将凸包上的第一个点P0和第二个点P1放入栈里面,然后将P1后面的点,即P2拿出来作为当前点,开始找第三个点:
- 4.连接P0和栈顶的那个点,得到直线L。看当前点是在直线L的左边还是右边。如果在右边(叉积大于0),执行步骤5,如果在直线上或是在左边(叉积小于等于0),就执行步骤6。
- 5.如果在右边,则栈顶的那个点P1不是凸包上的点,把栈顶元素出栈。执行步骤4.
- 6.当前点是凸包上的点,把它P2压入栈,执行步骤7.
- 7.检查当前点P2是不是步骤3中结果的最后一个元素。是,就结束。如果不是就把P2后面的点作为当前点,返回步骤4。
最后,栈中元素就是凸包上的点了。
2.Jarvis步进法
复杂度
时间复杂度:O(nH)。(其中n是点的总个数,H是凸包上的点的个数)思路:
- 找到纵坐标最小的点,这个点一定为凸包上的点,设为P0。
- 从P0开始,按逆时针的方向,逐个找凸包上的点,每前进一步找到一个点,所以叫做步进法。
- 利用夹角找下一个点。假设现在已经找到了{P0,P1,P2},要找下一个点:剩下的点分别和P2组成向量,设这个向量与向量P1P2的夹角为 β 。当 β 最小的时候,就是所要求的下一个点了。
注意:
- 1.找第二个点P1时,因为已经找到的只有P0一个点,所以向量只能和水平线做夹角 α ,当 α 最小时,求得第二个点。
- 2.共线情况:如果直线P2P3上还有一个点P4,即三个点共线,此时由向量P2P3和向量P2P4产生的两个
β
是相同的。我们应该把P3、P4都当做凸包上的点,并且把距离P2最远的那个点作为最后搜索到的点,继续找它的下一个连接点。
3.分治法
复杂度
时间复杂度:O(nlogn)。
思路:应用分治法思想,把一个大问题分成几个结构相同的子问题,把子问题进一步细化。然后利用递归的方法分别求子问题的解。步骤:
- 1.将所有点放入二维坐标系中,设横坐标最小的点为P0,横坐标最大的点为Pn。这两点一定是凸包上的点。直线P1Pn把点集分成两个部分,x轴上部叫做上包,下部叫做下包。
- 2.对上包:求距离直线P1Pn最远的点,即下图中的点Pmax。
- 3.作直线P1Pmax、PnPmax,把直线P1Pmax左侧的点当成是上包,把直线PnPmax右侧的点也当成是上包。
- 4.重复步骤2、3。
- 5.对下包也作类似操作。