根据极角排序Graham扫描法求凸包(但无法完全保留凸包边上的点)
1. 找出最左下的点(首先要最下,有多个最下则找最左),记为p[0]
2. 以p[0]为原点,对p[1]...p[n-1]进行按极角排序。极角相同的,距离p[0]近的算小3. 栈里面放入p[0],p[1],p[2]
for(int i = 3; i < n; ++i) {
while(true) {
考察栈顶元素 k2,k2下方元素k1,以及p[i]考虑 k1->k2->p[i]
if( 在k2这一点直走或者向右拐了) {
将k2出栈
else break;
}
p[i]入栈
}
4. 栈中的点就是凸包的所有顶点
//极角序 Graham扫描法
struct Point{
double x,y;
Point(double x,double y) :x(x),y(y){}
Vector operator - (const Point& p){
return Vector(x-p.x,y-p.y);
}
bool operator < (const Point& p) const { //注意是取下左点,最下中的最左
if(p.y==y) return x<p.x;
return y<p.y;
}
};
vector<Point> points;
vector<Point> stack;
double cross(Vector a,Vector b){
return a.x*b.y-a.y*b.x;
}
int Sign(double x){
if(fabs(x)<EPS) return 0;
return x<0?-1:1;
}
double dist(const Point& a,const Point& b){
return hypot(fabs(a.x-b.x),fabs(a.y-b.y));
}
struct cmp{
Point p0;
cmp(Point p):p0(p){}
bool operator () (Point p1,Point p2) const {
int s=Sign(cross(p1-p0,p2-p0));
if(s>0) return true;
else if(s<0) return false;
else {
if(dist(p1,p0)<dist(p0,p2)) return true;
else return false;
}
}
};
int Graham()
{
if(points.size()<3) return 0;
stack.clear();
sort(points.begin(),points.end()); //找出下左的点
sort(points.begin()+1,points.end(),cmp(points[0]));
stack.push_back(points[0]);
stack.push_back(points[1]);
stack.push_back(points[2]);
for(int i=3;i<points.size();i++)
{
for(;;){
Point p2=*(stack.end()-1);
Point p1=*(stack.end()-2);
if(Sign(cross(p2-p1,points[i]-p2))<=0) //小于零代表向右拐或直走
stack.pop_back();
else break;
}
stack.push_back(points[i]); //i点肯定是向右拐的
}
}
根据水平排序Graham扫描法求凸包(可以求出凸包边上的点,就是比极角序的麻烦)
判断的方法是否是凸包的方法和极角的一样,就是求一边只求出了一半的凸包,还要倒着扫一遍点
//水平序Graham扫描法求凸包
struct Point{
double x,y;
Point(double x,double y) :x(x),y(y){}
Vector operator - (const Point& p){
return Vector(x-p.x,y-p.y);
}
bool operator < (const Point& p) const { //注意取下左点
if(p.y==y) return x<p.x;
return y<p.y;
}
};
vector<Point> points;
vector<Point> stack;
double cross(Vector a,Vector b){
return a.x*b.y-a.y*b.x;
}
int Sign(double x){
if(fabs(x)<EPS) return 0;
return x<0?-1:1;
}
double dist(const Point& a,const Point& b){
return hypot(fabs(a.x-b.x),fabs(a.y-b.y));
}
int Graham()
{
if(points.size()<3) return 0;
stack.clear();
sort(points.begin(),points.end());
stack.push_back(points[0]);
stack.push_back(points[1]);
int n=points.size();
for(int i=2;i<n;i++)
{
while(stack.size()>1){ //一定要这一条,因为第二个点不一定是凸包可能会pop,而极角排序的肯定是凸包中的点
Point p2=*(stack.end()-1);
Point p1=*(stack.end()-2);
if(Sign(cross(p2-p1,points[i]-p2))<0) //不加等于,可以保留凸包上的点
stack.pop_back();
else break;
}
stack.push_back(points[i]);
}
int size=stack.size();
stack.push_back(points[n-2]); //因为此时栈顶元素是points[n-1],下一个就是n-2
for(int i=n-3;i>=0;i--)
{
while(stack.size()>size){
Point p2=*(stack.end()-1);
Point p1=*(stack.end()-2);
if(Sign(cross(p2-p1,points[i]-p2))<0)
stack.pop_back();
else break;
}
stack.push_back(points[i]);
}
stack.pop_back(); //points[0]被重复push两次了
}