凸包問題的五種解法

2021-07-11 15:54:26 字數 3944 閱讀 3685

前言:

首先,什麼是凸包? 

說凸包首先要說凸性的定義,簡單點說就是平面鄰域中任意兩點所在的線段上的點都在該鄰域中,則該鄰域具有凸性。簡單推敲一下,就可以發現如果鄰域中存在一階導數不連續的點一定無法被某點集線性表示出來。再往下的內容屬於數學分析了,對我們的演算法設計幫助不大,暫時先不管。

假設平面上有p0~p12共13個點,過某些點作乙個多邊形,使這個多邊形能把所有點都「包」起來。當這個多邊形是凸多邊形的時候,我們就叫它「凸包」。如下圖:  

然後,什麼是凸包問題?

我們把這些點放在二維座標系裡面,那麼每個點都能用 (x,y) 來表示。

現給出點的數目13,和各個點的座標。求構成凸包的點?

解一:窮舉法(蠻力法)

時間複雜度:o(n³)。

思路:兩點確定一條直線,如果剩餘的其它點都在這條直線的同一側,則這兩個點是凸包上的點,否則就不是。

步驟:將點集裡面的所有點兩兩配對,組成 n(n-1)/2 條直線。

對於每條直線,再檢查剩餘的 (n-2) 個點是否在直線的同一側。

如何判斷乙個點 p3 是在直線 p1p2 的左邊還是右邊呢?(座標:p1(x1,y1),p2(x2,y2),p3(x3,y3))

利用叉積

(x2-x1)*(y3-y1)-(x3-x1)*(y2-y1)       

當上式結果為正時,p3在直線 p1p2 的左側;當結果為負時,p3在直線 p1p2 的右邊。

解二:分治法

時間複雜度:o(n㏒n)。

思路:應用分治法思想,把乙個大問題分成幾個結構相同的子問題,把子問題再分成幾個更小的子問題……。然後我們就能用遞迴的方法,分別求這些子問題的解。最後把每個子問題的解「組裝」成原來大問題的解。

步驟:把所有的點都放在二維座標系裡面。那麼橫座標最小和最大的兩個點 p1 和 pn 一定是凸包上的點(為什麼呢?用反證法很容易證明,這裡不詳講)。直線 p1pn 把點集分成了兩部分,即 x 軸上面和下面兩部分,分別叫做上包和下包。

對上包:求距離直線 p1pn 最遠的點,即下圖中的點 pmax 。

作直線 p1pmax 、pnpmax,把直線 p1pmax 左側的點當成是上包,把直線 pnpmax 右側的點也當成是上包。

重複步驟 2、3。

對下包也作類似操作。

然而怎麼求距離某直線最遠的點呢?我們還是用到解一中的公式:

(x2-x1)*(y3-y1)-(x3-x1)*(y2-y1)     

設有乙個點 p3 和直線 p1p2 。(座標:p1(x1,y1),p2(x2,y2),p3(x3,y3))

對上式的結果取絕對值,絕對值越大,則距離直線越遠。

注意:在步驟一,如果橫座標最小的點不止乙個,那麼這幾個點都是凸包上的點,此時上包和下包的劃分就有點不同了,需要注意。

解三:jarvis步進法

時間複雜度:o(nh)。(其中 n 是點的總個數,h 是凸包上的點的個數)

思路:縱座標最小的那個點一定是凸包上的點,例如圖上的 p0。

從 p0 開始,按逆時針的方向,逐個找凸包上的點,每前進一步找到乙個點,所以叫作步進法。

怎麼找下乙個點呢?利用夾角。假設現在已經找到 了,要找下乙個點:剩下的點分別和 p2 組成向量,設這個向量與向量p1p2的夾角為 β 。當 β 最小時就是所要求的下乙個點了,此處為 p3 。

找第二個點 p1 時,因為已經找到的只有 p0 乙個點,所以向量只能和水平線作夾角 α,當 α 最小時求得第二個點。

解四:graham掃瞄法

時間複雜度:o(n㏒n)

思路:graham掃瞄的思想和jarris步進法類似,也是先找到凸包上的乙個點,然後從那個點開始按逆時針方向逐個找凸包上的點,但它不是利用夾角。

把所有點放在二維座標系中,則縱座標最小的點一定是凸包上的點,如圖中的p0。

把所有點的座標平移一下,使 p0 作為原點,如上圖。

計算各個點相對於 p0 的幅角 α ,按從小到大的順序對各個點排序。當 α 相同時,距離 p0 比較近的排在前面。例如上圖得到的結果為 p1,p2,p3,p4,p5,p6,p7,p8。我們由幾何知識可以知道,結果中第乙個點 p1 和最後乙個點 p8 一定是凸包上的點。

(以上是準備步驟,以下開始求凸包)

以上,我們已經知道了凸包上的第乙個點 p0 和第二個點 p1,我們把它們放在棧裡面。現在從步驟3求得的那個結果裡,把 p1 後面的那個點拿出來做當前點,即 p2 。接下來開始找第三個點:

連線p0和棧頂的那個點,得到直線 l 。看當前點是在直線 l 的右邊還是左邊。如果在直線的右邊就執行步驟5;如果在直線上,或者在直線的左邊就執行步驟6。

如果在右邊,則棧頂的那個元素不是凸包上的點,把棧頂元素出棧。執行步驟4。

當前點是凸包上的點,把它壓入棧,執行步驟7。

檢查當前的點 p2 是不是步驟3那個結果的最後乙個元素。是最後乙個元素的話就結束。如果不是的話就把 p2 後面那個點做當前點,返回步驟4。

最後,棧中的元素就是凸包上的點了。

以下為用graham掃瞄法動態求解的過程:

double cross(point t1,point t2,point t3,point t4) //求向量t1t2和向量t3t4的叉積

double dis(point t1,point t2) //求距離

bool cmp(point t1,point t2)

void findpoint() //找基點,按y從小到大,如果y相同,按x從左到右

}t=p[0];

p[0]=p[j];

p[j]=t;

}void scanner()

if(r > rmax)

}if(rmax <= 0)

}g_result[1][0] = x1;

g_result[1][1] = y1;

g_result[2][0] = x2;

g_result[2][1] = y2;

g_result[0][0] += 2;

getresult(point, x1, y1, x2, y2);

getresult(point, x2, y2, x1, y1);

printf("\n\n構成凸包的點有:\n");

for(i=1;i<=g_result[0][0];i++)

printf("(%d,%d)\n",g_result[i][0],g_result[i][1]);

}/* 輸入

1430 30

50 60

60 20

70 45

86 39

112 60

200 113

250 50

300 200

130 240

76 150

47 76

36 40

33 35

輸出(30,30)

(300,200)

(130,240)

(76,150)

(47,76)

(250,50)

(60,20)

*/擴充套件:以上討論的只是二維的凸包,二維凸包可以用來解決圍欄問題、城市規劃問題、聚類分析等等。

凸包問題的五種解法

首先,什麼是凸包?假設平面上有p0 p12共13個點,過某些點作乙個多邊形,使這個多邊形能把所有點都 包 起來。當這個多邊形是凸多邊形的時候,我們就叫它 凸包 如下圖 然後,什麼是凸包問題?我們把這些點放在二維座標系裡面,那麼每個點都能用 x,y 來表示。現給出點的數目13,和各個點的座標。求構成凸...

凸包問題的五種解法

首先,什麼是凸包?假設平面上有p0 p12共13個點,過某些點作乙個多邊形,使這個多邊形能把所有點都 包 起來。當這個多邊形是凸多邊形的時候,我們就叫它 凸包 如下圖 然後,什麼是凸包問題?我們把這些點放在二維座標系裡面,那麼每個點都能用 x,y 來表示。現給出點的數目13,和各個點的座標。求構成凸...

凸包問題的五種解法

首先,什麼是凸包?假設平面上有p0 p12共13個點,過某些點作乙個多邊形,使這個多邊形能把所有點都 包 起來。當這個多邊形是凸多邊形的時候,我們就叫它 凸包 如下圖 然後,什麼是凸包問題?我們把這些點放在二維座標系裡面,那麼每個點都能用 x,y 來表示。現給出點的數目13,和各個點的座標。求構成凸...