在网络上关于凸包解法多种多样,讲的也非常的不错,可以参考一下这篇博客,但是还是想用自己的话去描述一遍,以加深一下印象。
常见的解法有:
- 穷举法(蛮力法)
- 分治法
- Jarvis步进法
- Graham扫描法
- Melkman算法
本文主要讲一下Graham扫描法的步骤以及实现,先借一张动图来说明一下Graham扫描的算法的流程。
算法流程
- 找到相对左下角的点P0,为什么叫相对,因为我这里是先找到y轴最小的点,如果有相同,则选择X轴最小的点;
- 计算所有的点与P0的极角的大小,并由小到大的排序,如上面的动图所示排序好的顺序;
- 开始寻找凸包;
- 第一步,P0和P1与P2去构建向量P0P1和P0P2
,P1,P2,P3入栈
- 第二步,P1和P2出栈,和P3构建向量P1P2,P1P3
,所以P1和P3入栈
- 第三步,以此类推,P1,P3出栈和P4构建向量,当向量的叉乘积大于0时,三者都进栈,否则中间点出栈,即P3出栈,两边点进栈(P1和P4进栈)
算法源码
1.寻找P0点
Point findPointMin(Point pointIn[], int size)
{
Point pointOut = pointIn[0];
for (int i = 1; i < size; i++)
{
if (pointOut.y > pointIn[i].y)
{
pointOut = pointIn[i];
}
else if (abs(pointOut.y - pointIn[i].y) < 0.000001)
{
if (pointOut.x > pointIn[i].x)
{
pointOut = pointIn[i];
}
}
}
return pointOut;
}
2.排序
bool sortPoint(Point a, Point b)
{
bool stat = true;
if (P0.x == a.x && P0.y == a.y)
return true;
if (P0.x == b.x && P0.y == b.y)
return false;
float angle_a = a.x == P0.x ? MAXFLOAT : (a.y - P0.y) / (a.x - P0.x);
float angle_b = b.x == P0.x ? MAXFLOAT : (b.y - P0.y) / (b.x - P0.x);
float length_a = (a.y - P0.y) * (a.y - P0.y) + (a.x - P0.x) * (a.x - P0.x);
float length_b = (b.y - P0.y) * (b.y - P0.y) + (b.x - P0.x) * (b.x - P0.x);
if (abs(angle_a - angle_b) <= 0.000001)
{
stat = length_a > length_b ? false : true;
}
if (angle_a < 0 && angle_b >= 0)
{
stat = false;
}
else if (angle_a >= 0 && angle_b < 0)
{
stat = true;
}
else
{
if (angle_a < angle_b)
{
stat = true;
}
else
{
stat = false;
}
}
return stat;
}
3.寻找凸包
vector<Point> findConvexHull(Point pointIn[], int size)
{
stack<Point> resultPoint;
vector<Point> convexHull;
queue<Point> tempPoint;
float flag = 0;
resultPoint.push(pointIn[0]);
resultPoint.push(pointIn[1]);
for (int i = 2; i < size; i++)
{
// if(((pointIn[i-1].y - P0.y) * (pointIn[i].x - P0.x)) == (pointIn[i-1].x-P0.x) * (pointIn[i].y - P0.y))
// {
// continue;
// }
Point p3 = pointIn[i];
Point p2 = resultPoint.top();
resultPoint.pop();
Point p1 = resultPoint.top();
flag = (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y);
if (flag > 0)
{
resultPoint.push(p2);
resultPoint.push(p3);
}
else if (flag == 0)
{
resultPoint.push(p3);
}
else
{
while (1)
{
p2 = resultPoint.top();
resultPoint.pop();
p1 = resultPoint.top();
flag = (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y);
if (flag <= 0)
{
resultPoint.push(p3);
break;
}
else
{
resultPoint.push(p2);
resultPoint.push(p3);
break;
}
}
}
}
cout << "result point" << endl;
while (!resultPoint.empty())
{
Point temp = resultPoint.top();
resultPoint.pop();
cout << "(" << temp.x << "," << temp.y << ")" << endl;
convexHull.push_back(temp);
}
return convexHull;
}
主函数
int main()
{
vector<Point> convexHull;
/* 测试数据*/
// Point pointSet[10]={{1,2},{1,3},{0,3},{-1,3},{3,3},{4,3},{5,3},{-2,3},{6,3},{2,3}};
// Point pointSet[10] = {{1, 0}, {2, 1}, {1, 3}, {2, 2}, {1, 2}, {3, 1}, {3, 3}, {3, 2}, {4, 1}};
Point pointSet[10] = {{1, 0}, {1, 1}, {1, 3}, {2, 3}, {2, 2},{2,4}, {3, 3}, {3, 4}, {4, 3},{0,4}};
/* 寻找P0*/
P0 = findPointMin(pointSet, sizeof(pointSet) / sizeof(pointSet[0]));
cout << "P0:" << P0.x << " " << P0.y << endl;
/* 按极角排序*/
sort(pointSet, pointSet + 10, sortPoint);
/* 寻找凸包*/
convexHull = findConvexHull(pointSet, 10);
}