幾何問題中的分治法
1.最近對問題
問題描述:
設p1=(x1,y1),p2=(x2,y2),…,pn=(xn,yn)一共n個點構成點集S,最近點對問題就是找出集合中距離最近的兩個點,嚴格來說最近點對可能多於一個,但我們簡單起見只找出一對即可。
算法思路:
(1)劃分:將集合S分成兩個子集S1和S2,根據平衡子問題原則,每個子集中大約有n/2個點,設集合S的最近點對是pi與pj(1<=i,j<=n),則會出現三種情況。
①pi在S1中,pj在S1中
②pi在S2中,pj在S2中
③pi在S1中,pj在S2中
(2)劃分①②可以遞歸,劃分③比較麻煩
(3)合併,比較三種情況取最小值返回即可
下面討論情況③:
代碼:
import org.junit.Test;
public class AVL {
//最近點對問題
@Test
public void Test()
{
Point p1 = new Point();
p1.setX(0.1);
p1.setY(0.2);
Point p2 = new Point();
p2.setX(0.4);
p2.setY(0.3);
Point p3 = new Point();
p3.setX(0.2);
p3.setY(0.1);
Point points[]=new Point[]{p1,p2,p3};
Point[] points_help = new Point[points.length];
for(int i=0;i<points.length;i++){
points_help[i]=new Point();
}
//排序橫軸
sort(points,0,2,0);
//驗證sort函數
for (Point point : points) {
System.out.println(point);
}
System.out.println(MiniDisttance(points,0,2,points_help));
}
//編寫函數將點集按照排序,condition爲0表示按照橫座標排序,爲1表示按照縱座標排序
int Partition(Point points[],int left,int right,int condtion){
int i=left,j=right;
Point temp=new Point();
temp = points[i];
if(condtion==0)
while(i<j){
while(i<j&&points[j].x>=temp.x)
j--;
points[i]=points[j];
while(i<j&&points[i].x<=temp.x)
i++;
points[j]=points[i];
}
else{
while(i<j){
while(i<j&&points[j].y>=temp.y)
j--;
points[i]=points[j];
while(i<j&&points[i].y<=temp.y)
i++;
points[j]=points[i];
}
}
points[i]=temp;
return i;
}
void sort(Point points[],int left,int right,int condtion){
if(left<right){
int partition = Partition(points, left, right, condtion);
sort(points,left,partition-1,condtion);
sort(points,partition+1,right,condtion);
}
}
//在使用MiniDistance函數之前我們假設points已經按照x排好序了
double MiniDisttance(Point points[],int left,int right,Point points_help[]){
//如果只有兩個點返回即可
if(right-left==1){
getDistance(points[left],points[right]);
}//如果有三個點
else if(right-left==2){
double d1 = getDistance(points[left],points[left+1]);
double d2 = getDistance(points[left+1],points[left+2]);
if(d1<d2){
return d1;
}else
return d2;
}
int mid = (left+right)/2;
//最近點對在左半邊
double d1 = MiniDisttance(points,left,mid,points_help);
//最近點對在右半邊
double d2 = MiniDisttance(points,mid+1,right,points_help);
//比較d1與d2的大小
double d;
if(d1<d2)
d= d1;
else
d= d2;
//接下來尋找在mid左右兩邊都爲d範圍內的點並並將其保存
double mid_left = points[mid].x-d;
double mid_right = points[mid].y+d;
int index =0;
//收集P1與P2的點
for(int i=mid-1;i>=left;i--){
if(points[mid].x-points[i].x<=d)
points_help[index++]=points[i];
else
break;
}
for(int j=mid+1;j<=right;j++){
if(points[j].x-points[mid].x<=d){
points_help[index++]=points[j];
}
else
break;
}
//現在已經收集好了我們將其按照y軸的方向從小到大排序
sort(points_help,0,index-1,1);
for(int i=0;i<index;i++){
for(int j=i+1;j<index;j++){
if(getDistance(points_help[i],points_help[j])<d)
d=getDistance(points_help[i],points_help[j]);
else
break;
}
}
return d;
}
double getDistance(Point p1,Point p2){
return Math.sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
}
class Point{
public void setX(double x) {
this.x = x;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
public void setY(double y) {
this.y = y;
}
double x;
double y;
}
輸出:
2.凸包問題
問題描述:設p1=(x1,y1),p2=(x2,y2),…,pn=(xn,yn)是平面上n個點構成的集合S,凸包問題是爲集合S構造最小凸多邊形。
由於最近比較忙碌,就先介紹到這吧,後續有空會補上代碼