凸包
定义
平面上 N 个点的凸包是包围这 N 个点的最小多边形
应用
- 机器人运动路径规划:找到起点 S 到终点 T 之间的最短路径,而且需要避开一个多边形路障

s 和 t 之间的最短路径就是 s 和 t 之间的直线距离,或者凸包中上下折线中的其中之一
- 最远的两点:平面上有 N 个点,希望找到这 N 个点中,距离最远的那一个点对

距离最远的那一对点,一定在凸包上
属性
- 在凸包上,可以只通过做逆时针转弯而遍历闭凸包
- 以最小的 y 值点 p 为原点,与其他点的连线的极角逐渐增大
凸包算法-Graham scan
算法思想
Graham scan 算法思想是基于凸包的两个属性:
- 求取 N 个点中,y 值最小的点 p
- 将剩余的 N - 1 个点,按照与点 p 的极角值进行排序
- 遍历排序后的 N - 1 个点,只保留那些进行逆时针旋转的点
逆时针旋转点
现在有三个点,a, b, c。如何确定 a -> b -> c 是逆时针旋转点?
只有当 c 在 a -> b 连线的左侧的时候,c 才是逆时针旋转点

当 c 是逆时针旋转点的时候,数学上有:
ab⃗×bc⃗=[abxbcy−abybcx]
\vec {ab} \times \vec{bc} = \begin{bmatrix} ab_xbc_y-ab_ybc_x \end{bmatrix}
ab×bc=[abxbcy−abybcx]
其中, abxbcy - abybcx > 0
这个用右手法则也很好想,当 c 是逆时针旋转点的时候,从 ab 扫到 bc ,大拇指指向一定是正向;相反,则一定是反向;
代码实现
struct Point
{
float x;
float y;
Point() {
x = 0;
y = 0;
}
Point(float xi, float yi) {
x = xi;
y = yi;
}
bool operator==(const Point& other) {
if (std::abs(other.x - x) < 0.000001 && std::abs(other.y - y) < 0.0000001)
return true;
return false;
}
bool operator!=(const Point& other) {
return !(*this == other);
}
friend std::ostream& operator <<(std::ostream& out, const Point& p) {
out << p.x << ", " << p.y;
return out;
}
};
//return the cos value of the polar angle
float calPolarAngle(const Point& base, const Point& another) {
float xDistance = another.x - base.x;
float yDistance = another.y - base.y;
float distance = std::sqrtf(xDistance * xDistance + yDistance * yDistance);
return xDistance / distance;
}
struct YLess
{
bool operator()(const Point& lhs, const Point& rhs) {
return lhs.y < rhs.y;
}
};
struct PolarAngleLess
{
PolarAngleLess(const Point& p) : base(p) {}
bool operator()(const Point& lhs, const Point& rhs) {
return calPolarAngle(base, lhs) > calPolarAngle(base, rhs);
}
Point base;
};
bool ccwTurn(const Point& a, const Point& b, const Point& c) {
Point ab(b.x - a.x, b.y - a.y);
Point bc(c.x - b.x, c.y - b.y);
return ab.x*bc.y - ab.y*bc.x > 0;
}
std::vector<Point> convecHull(std::vector<Point>& points) {
if (points.size() <= 3) return points;
std::vector<Point> backPoints(points);
auto yMinItr = std::min_element(backPoints.begin(), backPoints.end(), YLess());
//put the yMinPoint to the front
std::iter_swap(yMinItr, backPoints.begin());
//sort the left n-1 points based polar angle
std::sort(backPoints.begin() + 1, backPoints.end(), PolarAngleLess(backPoints.front()));
for (auto it = backPoints.begin(); it != backPoints.end() - 2; ++it) {
while (!ccwTurn(*it, *(it + 1), *(it + 2))) {
backPoints.erase(it + 1);
it = it - 1;
}
}
return backPoints;
}
4893




被折叠的 条评论
为什么被折叠?



