Graham’s Scan 图示
Graham’s Scan 是先进行排序,然后再进行包围点,中间用到了栈,最为节点存储,这样就不必遍历所有点的方式来寻找最外围的点。
- 时间复杂度:O(NlogN) ,主要取决于排序的时间。
(其中寻找起点的时间 O(N) 。加上排序的时间 O(NlogN) 。加上包围的时间 O(N) :总共前进 N 次,最多倒退 N 次,共为 2N 次。)
具体思想:
- 排序,根据某个坐标轴为主进行排序
- 在边界上找到一个点作为起点
- 寻找附件2个点,将3个点依次压入栈中
- 对栈顶的2个点和点集中的其他点进行方向测试(可以利用叉积运算来判断)
- 如果是反方向的,则出栈,重复第4步
- 如果是正方向的,则进栈,重复第4步
- 直到所有点构成一个完整回路
Graham’s Scan 代码
long long int orientation(Point p, Point q, Point r)
{
double val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
if (val == 0) return 0;
return (val > 0)? 1: 2;
}
int compare(const void *vp1, const void *vp2)
{
Point *p1 = (Point *)vp1;
Point *p2 = (Point *)vp2;
long long int o = orientation(p0, *p1, *p2);
if (o == 0)
return (distSq(p0, *p2) >= distSq(p0, *p1))? -1 : 1;
return (o == 2)? -1: 1;
}
vpdd convexHull(Point points[], long long int n)
{
long long int ymin = points[0].y, min = 0;
for (long long int i = 1; i < n; i++)
{
long long int y = points[i].y;
if ((y < ymin) || (ymin == y &&
points[i].x < points[min].x))
ymin = points[i].y, min = i;
}
swap(points[0], points[min]);
p0 = points[0];
qsort(&points[1], n-1, sizeof(Point), compare);
vpdd output;
long long int m = 1;
for (long long int i=1; i<n; i++)
{
while (i < n-1 && orientation(p0, points[i], points[i+1]) == 0)
i++;
points[m] = points[i];
m++;
}
if (m < 3){ return output;
}
stack<Point> S;
S.push(points[0]);
S.push(points[1]);
S.push(points[2]);
for (int i = 3; i < m; i++)
{
while (orientation(nextToTop(S), S.top(), points[i]) != 2)
S.pop();
S.push(points[i]);
}
// Now stack has the output points, print contents of stack
//int i=0;
long long int len=S.size();
pair <double,double> Pair1 ;
while (!S.empty())
{
Point p = S.top();
Pair1.first=p.x;
Pair1.second=p.y;
//cout << p.x << ", " << p.y << endl;
output.push_back(Pair1) ;
S.pop();
}
//sort(output.begin(),output.end());
/*for(int i=0;i<len;i++){
cout << output[i].first << ", " << output[i].second << endl;
}*/
return output;
}