本文參考自<<算法導論>>章節33.3 尋找凸包
Graham算法主要利用向量的叉積判斷點和線段的位置關系,詳見 向量叉積,然后從左下角點按逆時針方向尋找最邊緣的線段,利用的原理就是從凸包上任意一點逆時針出發,每到一個節點,一定會向左拐.算法復雜度為O(nlg(n))
算法主要實現如下:
1 // 輸入:點數組arrInPt,個數nInPtCount,包含所有點 2 // 輸出:點數組arrOutPt,個數nOutPtCount,逆時針順序依次存放凸包上的點 3 static void Graham(Point arrInPt[],const int nInPtCount,Point arrOutPt[],int & nOutPtCount) 4 { 5 // step 1:找到最靠近坐下角的點,放到p[0]位置 6 // 保證p[0]的y值最小,若兩點y值相同,取x值較小者 7 int nIndex = 0; 8 for(int i = 1;i < nInPtCount;++i) 9 { 10 if(arrInPt[i].y < arrInPt[nIndex].y || 11 (arrInPt[i].y == arrInPt[nIndex].y && arrInPt[i].x < arrInPt[nIndex].x)) 12 { 13 nIndex = i; 14 } 15 } 16 Point tmp = arrInPt[0]; 17 arrInPt[0] = arrInPt[nIndex]; 18 arrInPt[nIndex] = tmp; 19 20 // step 2:剩下的點p[1]--p[nInPtCount-1],按照與p[0]極角升序排序 21 SortByPolarAngle(arrInPt,nInPtCount); 22 23 // step 3:棧操作 24 nOutPtCount = 0; 25 // 前三個點入棧 26 arrOutPt[nOutPtCount++] = arrInPt[0]; 27 arrOutPt[nOutPtCount++] = arrInPt[1]; 28 arrOutPt[nOutPtCount++] = arrInPt[2]; 29 for(int i = 3;i < nInPtCount;i++) 30 { 31 // 棧中最上面兩個點如果不與arrInPt[i]形成左轉,就進行出棧操作 32 while(nOutPtCount > 1 && IfTurnLeft(arrOutPt[nOutPtCount-2],arrOutPt[nOutPtCount-1],arrInPt[i]) == false ) 33 { 34 nOutPtCount--; 35 } 36 // arrInPt[i]入棧 37 arrOutPt[nOutPtCount++] = arrInPt[i]; 38 } 39 }
Jarvis算法與Graham算法類似也是從左下角的點開始,依次搜尋與邊界點極角最小的點.算法復雜度為O(hn),h為邊界點的個數
1 // 輸入:點數組arrInPt,個數nInPtCount,包含所有點 2 // 輸出:點數組arrOutPt,個數nOutPtCount,逆時針順序依次存放凸包上的點 3 static void Jarvis(Point arrInPt[],const int nInPtCount,Point arrOutPt[],int & nOutPtCount) 4 { 5 // step 1:找到最靠近坐下角的點,放到p[0]位置 6 // 保證p[0]的y值最小,若兩點y值相同,取x值較小者 7 nOutPtCount = 0; 8 int nIndex = 0; 9 for(int i = 1;i < nInPtCount;++i) 10 { 11 if(arrInPt[i].y < arrInPt[nIndex].y || 12 (arrInPt[i].y == arrInPt[nIndex].y && arrInPt[i].x < arrInPt[nIndex].x)) 13 { 14 nIndex = i; 15 } 16 } 17 Point tmp = arrInPt[0]; 18 arrInPt[0] = arrInPt[nIndex]; 19 arrInPt[nIndex] = tmp; 20 // step : 尋找與p[nIndex]極角最小的那個點 21 nIndex = 0; 22 do 23 { 24 arrOutPt[nOutPtCount++] = arrInPt[nIndex]; 25 nIndex = FindMinPolarAngle(arrInPt,nInPtCount,nIndex); 26 } while (arrInPt[0] != arrInPt[nIndex]); 27 }
經測試,兩種算法的效率差不多,10W個點以內,1s之內可以搞定,100w個點,Jarvis大約需要5秒多,Graham大約需要6s多,也就是h < log(n)時,Jarvis效率略優.測試機器為i5 3.10GHz
Graham效率:
Jarvis效率: