简介
凸包(Convex Hull)是一个计算几何(图形学)中的概念。
在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,…Xn)的凸组合来构造.
在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。
用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。
线段属性
三个问题:
1.已知两个有向线段$\mathop{p_0p_1}\limits^{→}$和$\mathop{p_0p_2}\limits^{→}$,如何判断关于点$p_0$从$\mathop{p_0p_2}\limits^{→}$到$\mathop{p_0p_1}\limits^{→}$的方向是顺时针还是逆时针?
2.已知两个有向线段$\mathop{p_0p_1}\limits^{→}$和$\mathop{p_1p_2}\limits^{→}$,如果我们从$\mathop{p_0p_1}\limits^{→}$经过$\mathop{p_1p_2}\limits^{→}$,在点$p_1$处需要左转还是右转?
3.怎么确定两个有向线段$\mathop{p_0p_1}\limits^{→}$和$\mathop{p_3p_4}\limits^{→}$是否相交?
以上都是在确定两个向量的关系,为了解决问题需要理解何为交叉积。
两个向量的交叉积计算可以用一个矩阵行列式来表达:
也可以理解为一个有正负的平行四边形面积。
对于$p_1×p_2$的计算结果:
大于0 : 关于点(0, 0),p2到p1的方向是顺时针
小于0 : 逆时针
等于0 : 共线
问题1
已知三个点的坐标为$p_0(x_0, y_0),p_1(x_1, y_1),p_2(x_2, y_2)$。
p0作为起点,计算交叉积:
$\mathop{p_0p_1}\limits^{→}=p_1-p_0=(x_1-x_0,y_1-y_0)$
$\mathop{p_0p_2}\limits^{→}=p_2-p_0=(x_2-x_0,y_2-y_0)$
$\mathop{p_0p_1}\limits^{→}×\mathop{p_0p_2}\limits^{→}=(x_1-x_0)(y_2-y_0)-(x_2-x_0)(y_1-y_0)$
大于0 : 顺时针
小于0 : 逆时针
等于0 : 共线
问题2
通过交叉积可以直接判断。
计算$(p_2-p_0)×(p_1-p_0)$
大于0 : 右转
小于0 : 左转
等于0 : 共线
问题3
线段相交可分为两种情况,一种是交点出现在线段中,一种交点出现在端点。
第二种情况比较特殊,需要针对每个点进行判断,直接看一下伪代码:
1 | //p表示点坐标,该函数求向量p_kp_j到p_kp_i的方向。 |
Graham-Scan算法
算法步骤:
集合Q中有m个点,找出y坐标最小的点$p_0$。
Q剩下的点按照关于$p_0$的极角大小逆时针排序得到$p_1…p_m$。
创建栈S来存储凸包的顶点,Push($p_0$,S),Push($p_1$,S),Push($p_2$,S)。
按顺序遍历剩下的点->
for i = 3…m
while(关于Top(S)的前一个点,经过Top(S)到$p_i$的转动方向非左转)
Pop(S)
Push($p_i$,S)
return S
整个算法的大致过程如下图所示:
模板代码
以前在kuangbin大神的博客学习过,所以我就直接粘贴他的模板了。
1 |
|
Jarvis-March算法
算法思想:
集合Q中有m个点,找出x和y坐标最小的点$p_0$作为当前凸包顶点k。
do {
for 非k的其余顶点
找到一个顶点s,使得集合Q中点都在ks线段的左侧
记录顶点s作为凸包的顶点
k=s
} while (k != $p_0$)
整个算法的大致过程如下图所示:
上图提供了一个算法的简略过程,关键步骤还是怎么确定所有集合点在线段的左侧。
模板代码
1 |
|