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;
    }
};

 

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