凸包
通常上讲
平面上有很多的点, 用一个 皮筋 圈住 这所有的点, 此时这个 皮筋, 就是 这些点的 凸包
数学上讲
性质1: 凸包上, 任何一条边 他所在的直线 即为L, 则所有的点 都在直线L 的 同一侧
比如以凸包上的 AB
边 为例, 所有的点: (要么在AB直线上) (要么都在同一侧比如以A->B这个方向来看, 都在该向量的 右侧
)
性质2 (重点):
-
由于凸包是一个环, 存在(顺时针 和 逆时针) 两个方向, 不妨先统一规定下 方向, 比如以 顺时针来看.
-
凸包上 任意连续的3个点
ABC
, 由于是顺时针, 我们看A -> B 这个方向的 向量
你让这个向量, 以A点
为中心 顺时针的旋转, 直到他 遇到 第一个点C
, 则, 该点C
, 就是 继AB后 凸包上的 下一个点
性质3: 还是以 顺时针为例
- 对于凸包上的 任意一个点
A
(即, 已知, A点 是凸包上的点), 设B
点是 A点 顺时针 的 下一个点. 记其他的(除了AB点) 的所有点 为Set集合
记:A -> B向量
为 vec1,A -> Set 所构成的 所有向量
为 vec2
则一定有:vec1 * vec2 (叉积)
<= 0 - 从上图可以看出, 已知A点 是凸包上的点
假设C点, 是A点 顺时针的 下一个点;
但是, 存在B点, 使得:A->C * A->B 的叉积
> 0; 说明: C点, 不是A点的 下一点
A->B向量 * A->other向量 的叉积
均是 <= 0 的. 说明: B点, 是A点的 下一个凸包上的点
这个性质, 是我们 获取凸包 (即获取凸包上所有的点), 算法的 核心依据.
因为这个性质 告诉我们, 如何通过 一个 凸包上的点, 找到 他的 下一个凸包上的点.
算法1 O(n * m)
统一以: 顺时针 为方向
先获取一个凸包上的点Start
O(n)遍历
, 找到 对{x,y}sort的 最小/最大的点 无需sort, O(n)cmp即可
这个点, 可以证明, 他一定是凸包上的, 因为他是 极值点
;
已知一个凸包上的点A, 如何得到他的下一个点B
Start点; ' 也就是上面的, 凸包的Start点 '
A点; ' 已经得到的凸包上的 顺时针的 最后一个点 '
B点 = 任意一个 {
非A}的点
FOR( C : 所有点){
if( {
A->C向量} 是在 {
A->B向量} 左侧){
B = C; ' 更新B '
}
}
if( B == Start){
' 这里很重要, 说明 又回到起点了 '
break;
}
具体代码
__VE< int> convex;
convex.clear();
{
int j = 0;
__FOR(i, 1, n-1, 1){
if( -1 == Db_cmp( points[i].x, points[j].x, eps)){
j = i;
}
else if( 0 == Db_cmp( points[i].x, points[j].x, eps)