LeetCode 587. 安装栅栏【凸包算法】【C++】【很多坑】

凸包算法不难理解,写代码的时候主要是几何上的判定条件很容易写错。

算法的总体思想是:

1.给所有的点排序,找出极点(纵座标最小的点,如果纵座标一样,取横座标最小的点)

2.除了极点之外,所有其他的点排序,排序的方式是与极点之间的夹角从小到大(如果夹角一样大,取距离极点近的排在前面)

3.把极点和第二个点放进栈,判断第三个点是否在这两个点的左边,如果在左边,进栈,否则,让第二个点出栈,一直进行到最后一个点

4.把所有与极点、最后一个点共线(三点一线,并且夹在两点中间)的点也放进栈里

关于栈的用法:

我原本用的是stack,但是这个容器不好取倒数第二个数,就换成了vector,用一个top变量来模拟栈顶。

在类中使用sort函数的话,自定义的比较函数不能放在类里面,否则会报错reference to non-static member function must be called: sort

vector<int> polar_point;
int polar_x, polar_y;
bool isPolar(vector<int> &a, vector<int> &b){
    if(a[1]==b[1]){//如果纵座标相等,判断横座标
        return a[0]<b[0];
    }
    return a[1]<b[1];//判断纵座标
}

double dist(vector<int> &a,vector<int> &b){
    return pow(double(pow((a[0]-b[0]),2)+pow((a[1]-b[1]),2)),0.5);
}

bool smallerAngle(vector<int> &a, vector<int> &b){
    if(abs(atan2(a[1]-polar_y,a[0]-polar_x)-atan2(b[1]-polar_y,b[0]-polar_x))<1e-8){//这边注意不能比较浮点数是否相等,只能取一个范围
        return dist(a,polar_point)<dist(b,polar_point);
    }
    return atan2(a[1]-polar_y,a[0]-polar_x)<atan2(b[1]-polar_y,b[0]-polar_x);
}

bool isLeft(vector<int> cur, vector<int> polar, vector<int> top){
    int cross = (cur[0]-polar[0])*(top[1]-polar[1])-(top[0]-polar[0])*(cur[1]-polar[1]);//x1y2-x2y1 计算叉积
    if(cross>0)return false;
    else if(cross<0)return true;
    else{//叉积等于0的时候,有可能是反向的,要判断一下
        bool codirect = ((cur[0]-polar[0])*(top[0]-polar[0]))>0||((cur[1]-polar[1])*(top[1]-polar[1]))>0;//不能只用一个座标x来判断,x有可能是0
        return codirect;
    }
}

bool isLine(vector<int> &a,vector<int> &b, vector<int> &c){
    bool coline = ((c[1]-a[1])*(a[0]-b[0]))==((a[1]-b[1])*(c[0]-a[0]));
    bool inbetween = (b[0]-a[0])*(c[0]-a[0])<0||(b[1]-a[1])*(c[1]-a[1])<0;//不能只用一个座标x来判断,x有可能是0
    return coline&&inbetween;//(y3-y1)*(x1-x2)==(y1-y2)*(x3-x1) a在bc中间
}

class Solution {
public:
    vector<vector<int>> outerTrees(vector<vector<int>>& points) {
        int n = points.size();//点的个数
        if(n==0||n==1||n==2||n==3){return points;}//简单情况直接处理
        sort(points.begin(),points.end(),isPolar);//排序找极点
        polar_point = points[0];//找到了极点
        polar_x = polar_point[0];polar_y = polar_point[1];
        sort(points.begin()+1,points.end(),smallerAngle);//基于极点排序
        vector<vector<int>> res;
        res.push_back(polar_point);
        int i = 1;
        res.push_back(points[i++]);
        int top = 1;
        while(i<n){//遍历所有的点
            if(isLeft(points[i],res[top-1],res[top])){//如果扫描到的点构成凸包
                res.push_back(points[i++]);
                top++;
            }
            else{//如果扫描到的点不构成凸包
                res.pop_back();
                top--;
            }
        }
        i = 1;
        while(i<n-1){//可能会漏掉的一部分点,介于极点与最后一个点之间的点,也是需要的
            if(isLine(points[i],points[0],points[n-1])&&find(res.begin(),res.end(),points[i])==res.end()){//要判断是否已经在答案里了
                res.push_back(points[i]);
            }
            i++;
        }
        return res;
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章