目錄
1 問題描述
給定一個平面上n個點的集合,它的凸包就是包含所有這些點的最小凸多邊形,求取滿足此條件的所有點。
另外,形象生動的描述:
(1)我們可以把這個問題看作如何用長度最短的柵欄把n頭熟睡的老虎圍起來。
(2)也可以這樣看:請把所討論的點想象成釘在膠合板上的釘子,膠合板代表平面。撐開一根橡皮筋圈,把所有的釘子都圍住,然后啪一聲松開手。凸包就是以橡皮圈為邊界的區域。具體示意如下圖1所示:
圖1 用橡皮筋來解釋凸包
2 解決方案
2.1 蠻力法
使用蠻力法解決此問題比較簡單,具體思想:對於一個n個點集合中的兩個點p1和p2,當且僅當該集合中的其它點都位於穿過這兩點的直線的同一邊時,它們的連線就是該集合凸包邊界的一部分,簡言之,p1和p2就是凸包問題中最小凸多邊形的頂點。對每一對點都做一遍檢驗之后,滿足條件的線段就構成了該凸包的邊界。
此時,根據上面的公式,我們只需要把每個點代入公式ax+by-c,判斷公式計算結果的符號是否全部大於等於0或者小於等於0,如果是則是凸包邊界上的點,否則就不是。該算法的時間效率為0(n^3)。具體代碼如下:
package com.liuzhen.chapterThree; public class ConvexHull { //蠻力法解決凸包問題,返回點集合中凸多邊形的點集合 public static Point[] getConvexPoint(Point[] A){ Point[] result = new Point[A.length]; int len = 0; //用於計算最終返回結果中是凸包中點的個數 for(int i = 0;i < A.length;i++){ for(int j = 0;j < A.length;j++){ if(j == i) //除去選中作為確定直線的第一個點 continue; int[] judge = new int[A.length]; //存放點到直線距離所使用判斷公式的結果 for(int k = 0;k < A.length;k++){ int a = A[j].getY() - A[i].getY(); int b = A[i].getX() - A[j].getX(); int c = (A[i].getX())*(A[j].getY()) - (A[i].getY())*(A[j].getX()); judge[k] = a*(A[k].getX()) + b*(A[k].getY()) - c; //根據公式計算具體判斷結果 } if(JudgeArray(judge)){ // 如果點均在直線的一邊,則相應的A[i]是凸包中的點 result[len++] = A[i]; break; } } } Point[] result1 = new Point[len]; for(int m = 0;m < len;m++) result1[m] = result[m]; return result1; } //判斷數組中元素是否全部大於等於0或者小於等於0,如果是則返回true,否則返回false public static boolean JudgeArray(int[] Array){ boolean judge = false; int len1 = 0, len2 = 0; for(int i = 0;i < Array.length;i++){ if(Array[i] >= 0) len1++; } for(int j = 0;j < Array.length;j++){ if(Array[j] <= 0) len2++; } if(len1 == Array.length || len2 == Array.length) judge = true; return judge; } public static void main(String[] args){ Point[] A = new Point[8]; A[0] = new Point(1,0); A[1] = new Point(0,1); A[2] = new Point(0,-1); A[3] = new Point(-1,0); A[4] = new Point(2,0); A[5] = new Point(0,2); A[6] = new Point(0,-2); A[7] = new Point(-2,0); Point[] result = getConvexPoint(A); System.out.println("集合A中滿足凸包的點集為:"); for(int i = 0;i < result.length;i++) System.out.println("("+result[i].getX()+","+result[i].getY()+")"); } }
上面定義的點Point類代碼如下:
package com.liuzhen.chapterThree; public class Point { private int x; private int y; Point(){ x = 0; y = 0; } Point(int x, int y){ this.x = x; this.y = y; } public void setX(int x){ this.x = x; } public int getX(){ return x; } public void setY(int y){ this.y = y; } public int getY(){ return y; } }
運行結果:
集合A中滿足凸包的點集為: (2,0) (0,2) (0,-2) (-2,0)