轉自http://blog.csdn.net/bone_ace/article/details/46239187
0 引
所謂凸包即選定點的連線組成的多邊形能夠將其它所有的點都包進來,這些點的連線組成的多邊形即稱之爲凸包,如下圖所示。凸包的作用可以是求解目標區域的輪廓,圖像分割等圖像高級形態學的操作。
1 數學基礎
如何判斷一個點 p3 是在直線 p1p2 的左邊還是右邊呢?(座標:p1(x1,y1),p2(x2,y2),p3(x3,y3))
當上式結果爲正時,p3在直線 p1p2 的左側;當結果爲負時,p3在直線 p1p2 的右邊。
2 Graham 算法
時間複雜度:O(n㏒n)
思路:Graham掃描的思想和Jarris步進法類似,也是先找到凸包上的一個點,然後從那個點開始按逆時針方向逐個找凸包上的點,但它不是利用夾角。
步驟:
1 把所有點放在二維座標系中,則縱座標最小的點一定是凸包上的點,如圖中的P0。
[ 當縱座標相同時,則取其中橫座標最小的點]
[ 這是基於極座標系算法的選取規則,另外一種常見的方法是選擇最左邊的,對應的是笛卡爾座標系 ]
2 把所有點的座標平移一下,使 P0 作爲原點,如上圖。
3 計算各個點相對於 P0 的幅角 α ,按從小到大的順序對各個點排序。當 α 相同時,距離 P0 比較近的排在前面。例如上圖得到的結果爲 P1,P2,P3,P4,P5,P6,P7,P8。我們由幾何知識可以知道,結果中第一個點 P1 和最後一個點 P8 一定是凸包上的點。
(以上是準備步驟,以下開始求凸包)
以上,我們已經知道了凸包上的第一個點 P0 和第二個點 P1,我們把它們放在棧裏面。現在從步驟3求得的那個結果裏,把 P1 後面的那個點拿出來做當前點,即 P2 。接下來開始找第三個點:
4 連接P0和棧頂的那個點,得到直線 L 。看當前點是在直線 L 的右邊還是左邊。如果在直線的右邊就執行步驟5;如果在直線上,或者在直線的左邊就執行步驟6。
5 如果在右邊,則棧頂的那個元素不是凸包上的點,把棧頂元素出棧。執行步驟4。
6 當前點是凸包上的點,把它壓入棧,執行步驟7。
檢查當前的點 P2 是不是步驟3那個結果的最後一個元素。是最後一個元素的話就結束。如果不是的話就把 P2 後面那個點做當前點,返回步驟4。
最後,棧中的元素就是凸包上的點了。
[ 上述算法,並不是求得最小凸包的算法,因爲步驟4中,始終是以P0與棧頂元素的連線去判斷以上圖爲例,去判斷,顯然最後得到的是P0-P3-P4-P6-P7-P8的凸包,而事實上,應該是沒有P4的。所以修正後的,應該是以棧中倒數第2個元素到棧頂元素的向量爲基準來判斷 當前點是在左側還是右側 ]
[ 步驟4時,是以左右比較通俗的語言描述,事實上從向量角度出發,即是判斷次頂元素、棧頂元素,當前點,組成的兩個向量的夾角,而上述求左右兩邊的行列式其實是等價於求兩個向量的叉乘結果]
3 其它算法
解一:窮舉法(蠻力法)
時間複雜度:O(n³)。
思路:兩點確定一條直線,如果剩餘的其它點都在這條直線的同一側,則這兩個點是凸包上的點,否則就不是。
步驟:
將點集裏面的所有點兩兩配對,組成 n(n-1)/2 條直線。
對於每條直線,再檢查剩餘的 (n-2) 個點是否在直線的同一側。
如何判斷一個點 p3 是在直線 p1p2 的左邊還是右邊呢?(座標:p1(x1,y1),p2(x2,y2),p3(x3,y3))
當上式結果爲正時,p3在直線 p1p2 的左側;當結果爲負時,p3在直線 p1p2 的右邊。
解二:分治法
時間複雜度:O(n㏒n)。
思路:應用分治法思想,把一個大問題分成幾個結構相同的子問題,把子問題再分成幾個更小的子問題……。然後我們就能用遞歸的方法,分別求這些子問題的解。最後把每個子問題的解“組裝”成原來大問題的解。
步驟:
把所有的點都放在二維座標系裏面。那麼橫座標最小和最大的兩個點 P1 和 Pn 一定是凸包上的點(爲什麼呢?用反證法很容易證明,這裏不詳講)。直線 P1Pn 把點集分成了兩部分,即 X 軸上面和下面兩部分,分別叫做上包和下包。
對上包:求距離直線 P1Pn 最遠的點,即下圖中的點 Pmax 。
作直線 P1Pmax 、PnPmax,把直線 P1Pmax 左側的點當成是上包,把直線PnPmax 右側的點也當成是上包。
重複步驟 2、3。
對下包也作類似操作。
然而怎麼求距離某直線最遠的點呢?我們還是用到解一中的公式:
設有一個點 P3 和直線 P1P2 。(座標:p1(x1,y1),p2(x2,y2),p3(x3,y3))
對上式的結果取絕對值,絕對值越大,則距離直線越遠。
注意:在步驟一,如果橫座標最小的點不止一個,那麼這幾個點都是凸包上的點,此時上包和下包的劃分就有點不同了,需要注意。
解三:Jarvis步進法
時間複雜度:O(nH)。(其中 n 是點的總個數,H 是凸包上的點的個數)
思路:
縱座標最小的那個點一定是凸包上的點,例如圖上的 P0。
從 P0 開始,按逆時針的方向,逐個找凸包上的點,每前進一步找到一個點,所以叫作步進法。
怎麼找下一個點呢?利用夾角。假設現在已經找到 {P0,P1,P2} 了,要找下一個點:剩下的點分別和 P2 組成向量,設這個向量與向量P1P2的夾角爲 β 。當 β 最小時就是所要求的下一個點了,此處爲 P3 。
注意:
找第二個點 P1 時,因爲已經找到的只有 P0 一個點,所以向量只能和水平線作夾角 α,當 α 最小時求得第二個點。
共線情況:如果直線 P2P3 上還有一個點 P4,即三個點共線,此時由向量P2P3 和向量P2P4 產生的兩個 β 是相同的。我們應該把 P3、P4 都當做凸包上的點,並且把距離 P2 最遠的那個點(即圖中的P4)作爲最後搜索到的點,繼續找它的下一個連接點。
解五:Melkman算法
說真的,這個算法我也還沒有看清。網上的資料也少的可憐,我暫且把網上的解釋截個圖在這裏,往後搞懂以後再回來補上。 或者有人看懂了的,希望不吝指教,不甚感激!