求點集中的最近點對有以下兩種方法:
設p1=(x1, y1), p2=(x2, y2), …, pn=(xn, yn)是平面上n個點構成的集合s,設計演算法找出集合s中距離最近的點對。
解體思路
1、蠻力法(適用於點的數目比較小的情況下)
1)演算法描述:已知集合s中有n個點,一共可以組成n(n-1)/2對點對,蠻力法就是對這n(n-1)/2對點對逐對進行距離計算,通過迴圈求得點集中的最近點對:
2)**描述:
double mindistance = double.maxvalue; //設定乙個mindistance儲存最近點對的距離,初始值為無窮大
int pointindex1,pointindex2; //設定pointindex1,pointindex2分別儲存最近點對的兩個點編號
for (i=1; i< n; i++) //迴圈計算n(n-1)/2對點對的距離
3)演算法時間複雜度:演算法一共要執行 n(n-1)/2次迴圈,因此演算法複雜度為o(n2)
2、分治法
1)演算法描述:已知集合s中有n個點,分治法的思想就是將s進行拆分,分為2部分求最近點對。演算法每次選擇一條垂線l,將s拆分左右兩部分為sl和sr
,l一般取點集s中所有點的中間點的x座標來劃分,這樣可以保證sl和sr中的點數目各為n/2,
(否則以其他方式劃分s,有可能導致sl和sr
中點數目乙個為1,乙個為n-1,不利於演算法效率,要盡量保持樹的平衡性)
依次找出這兩部分中的最小點對距離:δ
l和δr,記sl和sr
中最小點對距離δ= min(δ
l,δr),如圖1:
以l為中心,δ為半徑劃分乙個長帶,最小點對還有可能存在於sl和sr的交界處,如下圖2左圖中的虛線帶,p點和q點分別位於sl和sr
的虛線範圍內,在這個範圍內,p點和q點之間的距離才會小於δ,最小點對計算才有意義。
對於sl虛框範圍內的p點,在sr
虛框中與p點距離小於δ的頂多只有六個點,就是圖二右圖中的2個正方形的6的頂點。這個可以反推證明,如果右邊這2個正方形內有7個點與p點距離小於δ,例如q點,則q點與下面正方形的四個頂點距離小於δ,則和δ為sl
和sr中的最小點對距離相矛盾。因此對於sl虛框中的p點,不需求出p點和右邊虛線框內所有點距離,只需計算sr中與p點y座標距離最近的6個點,就可以求出最近點對,節省了比較次數。
(否則的話,最壞情形下,在sr虛框中有可能會有n/2個點,對於sl虛框中的p點,每次要比較n/2次,浪費了演算法的效率)
**描述:
1)對點集s的點x座標進行公升序排序,獲得點集point array
2)令δ=∞; //δ為最大點位距離
3)divide_conquer(point array,left,right) //分治法
if (left == right) then δ=∞; //如果point中只有乙個點,則δ=∞
return δ;
else if (right - left ==1) // point中只有2個點,則直接求這兩個點的距離
δ=d(sx.[0],)sx.[1]);
return δ;
else //如果point中多於2個點,則將point分治,以中心點畫線,將point分為左右兩部分a和
b,mid = (leftindex + rightindex)>>1; //mid為當前段中的中間點index
double d1 = closest_pair(leftindex,mid);
double d2 = closest_pair(mid +1,rightindex);
d =min(d1,d2);
listleftlist = new ();
listlrightlist = new ();
// 分離出寬度為d,距point[mid]<=d的區間,其實也就是化了兩條平行於x= point[mid].x的豎線。(之後還需要根據左邊a的p,在右邊選距離p.y<=d,在化兩條橫線,這樣乙個動態的矩形也就化出來了。(矩形包含兩個正方形,一條邊重合,也就是最多存在6(頂)點(鴿巢原理),可能滿足條件 (如上面的圖figure2)
迴圈遍歷 陣列
1. if (fabs(point[mid].x -point[i].x) <=d && i <= mid)
leftlist.add(point[i] //左邊符合條件的點
2. if (fabs(point[mid].x -point[i].x) <=d && i > mid)
rightlist.add(point[i]) //右邊符合條件的點
// 線性掃瞄
foreach ( point leftpoint in leftlist)
}
return d;
詳細的**如下:
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
double x;
public double x
set
}double y;
public double y
set
}public static void copy (point a, point b)
bool compyx(point a, point b)
return a.y < b.y;
}public static double dis(point a, point b)
}public static class sorthelper
}
public static void mergebyx(point array, int increasement)
int lastindex = array.length - 1;
int l1 = 0; //第1個有序表的起始位置
int h1 = 0; //第1個有序表的結束位置
int l2 = 0; //第2個有序表的起始位置
int h2 = 0; //第2個有序表的結束位置
int m = 0; //臨時表的初始位置
// 注意這裡的臨界條件(l2要存在,l2的index是: l1 + increasement<=lastindex)
while (l1 + increasement <= lastindex)
else //第2個有序表記錄的關鍵碼小於第1個有序表記錄的關鍵碼
}//第1個有序表中還有記錄沒有排序完
while (i <= h1)
//第2個有序表中還有記錄沒有排序完
while (j <= h2)
l1 = h2 + 1;
}//原順序表中還有記錄沒有排序完
while (l1 <= lastindex)
//臨時順序表中的記錄複製到原順序表,使原順序表中的記錄有序
for (int i = 0; i < array.length; i++)
}
}public class calculatehelper
public double getclosetdistant(point array, int leftindex, int rightindex)
// 情況二:當前區域只有兩個點時,直接返回這兩個點的距離 (出口二)
if (leftindex + 1 == rightindex)
// 按照x排序
sorthelper.mergesortbyx(array);
// 情況三,當前區域點的個數大於2的時候,需要採用分治法,把當前區域分成左右兩個部分,直到滿足情況一或二
int midindex = (leftindex + rightindex) >> 1;
double leftdistance = getclosetdistant(array, leftindex, midindex);
double rightdistance = getclosetdistant(array, midindex + 1, rightindex);
distant = math.min(leftdistance, rightdistance); //求出左右兩邊區域的最小距離
if (distant < 0.43)
for (int i = leftindex; i <= midindex; i++) //遍歷左邊的點
if (distant < 0.43)}}
}}if (distant < 0.43)
return distant;}}
}// 主函式
static void main(string args)
《程式設計之美》 尋找最近點對
問題 給定平面上兩個點的座標,找出距離最近的兩個點。分析與解法 一般思路是蠻力演算法,兩兩求n個點之間的距離並比較,得出最大距離,時間複雜度為o n 2 優化演算法是採用分治的思想,將n個點先按照 x 軸的座標的中值 x mx 平均分為左右兩組,分別求出 xl 和 xr 中的最近點對的距離,因為最近...
尋找最近點對
一維的數很簡單,先排序,再掃瞄已排好的數,相鄰兩個進行比較即可,時間複雜度為o n log2n n o n log2n 兩維的話 把平面上n個點分成兩部分left和right。假設分別求出left和right兩部分最短距離mindistleft和mindistright,還有一種情況就是點對中乙個點...
尋找最近點對
問題 在空間中有n個點,尋找空間中最近的2個點。法一 遍歷,o n 2 法二 分治演算法 將點分為左右兩半,分別找到最近的2個點,然後考慮交叉位置的點對中的最小距離,在這3者中取最小的那個。o nlogn 步驟 1.按照x軸排序 2.找到中間點,分別進行處理 3.2邊處理完成,將進行 merge 過...