# Convex Hull 凸包演算法 - 使用 `vector<int>` 來記錄點 `(x,y)` ```clike= // positive if clockwise // negative if counter clockwise int orientation(vector<int> &p, vector<int> &q, vector<int> &r) { int pqx = q[0]-p[0], pqy = q[1]-p[1]; int qrx = r[0]-q[0], qry = r[1]-q[1]; return pqy*qrx - pqx*qry; } int distance(vector<int> &p, vector<int> &q) { return (p[0]-q[0])*(p[0]-q[0]) + (p[1]-q[1])*(p[1]-q[1]); } ``` - 1.Javis' March - 1.先任意找凸包上的一個點,這裡取 x 最小的點 - 2.逆時針把凸包上的點找出 - 總時間複雜度 O(NC) ,其中 C 為凸包頂點數 - 以下不考慮共線問題,需另外處理 ```clike= vector<vector<int>> Javis(vector<vector<int>>& points) { int n = points.size(); if(n < 4) return points; vector<vector<int>> ans; int start = 0; for(int i=0;i<n;i++) if(points[i][0]<points[start][0]) start=i; int p = start; while(1){ int q = (p+1) % n; for(int i=0;i<n;i++) if(orientation(points[p],points[q],points[i])<0) q = i; ans.push_back(points[q]); p = q; if(p==start) break; } return ans; } ``` - 2.Granham Scan - 1.先將所有點依照逆時針順序排好 - 2.再來做繞行一圈的動作,每三個點判斷 - 總時間複雜度 O(NlogN) ,主要取決於排序的時間 ```clike= vector<vector<int>> Granham(vector<vector<int>>& points) { int n = points.size(); if(n < 4) return points; // pivot 取 y 最小的點,當作排序基準 vector<int> pivot = points[0]; for(int i=0;i<n;i++) if(points[i][1] < pivot[1]) pivot = points[i]; // 先把 points 按照角度排序,若角度相等照長度小到大 sort(points.begin(),points.end(),[&](vector<int> p, vector<int> q){ if(p[0]==pivot[0]&&p[1]==pivot[1]) return true; if(q[0]==pivot[0]&&q[1]==pivot[1]) return false; if(orientation(pivot, p, q)==0) return distance(pivot, p) < distance(pivot, q); else return orientation(pivot, p, q) < 0; }); // 最後一條線照長度大到小 int t = n-2; while(t>0 && orientation(pivot, points[t], points[n-1])==0) t--; for(int l=t+1, r=n-1;l<r;l++,r--) swap(points[l],points[r]); // Graham Scan,每三個點判斷順逆時針 vector<vector<int>> ans = {points[0], points[1]}; for(int j=2;j<n;j++){ while(ans.size()>=2 && orientation(ans[ans.size()-2], ans[ans.size()-1], points[j]) > 0) ans.pop_back(); ans.push_back(points[j]); } return ans; } ``` - 3.Monotone Chain - 1.先把點照 x 排序 - 2.先找出上凸包,再找出下凸包 - 總時間複雜度 O(NlogN) ,主要取決於排序的時間 ```clike= vector<vector<int>> MonotoneChain(vector<vector<int>>& points) { int n = points.size(); if(n < 4) return points; sort(points.begin(),points.end()); vector<vector<int>> ans; for(int i=0;i<n;i++){ // upper convex hull while (ans.size()>=2 && orientation(ans[ans.size()-2], ans[ans.size()-1], points[i]) < 0) ans.pop_back(); ans.push_back(points[i]); } if (ans.size() == n) return ans; // one side is enough ans.pop_back(); for(int i=n-1;i>=0;i--){ // lower convex hull while (ans.size()>=2 && orientation(ans[ans.size()-2], ans[ans.size()-1], points[i]) < 0) ans.pop_back(); ans.push_back(points[i]); } ans.pop_back(); return ans; } ``` - 4.其他還有 divide and conquer ,線上維護等方法