老师刚刚布置了个作业,让用quicksort实现凸包。但是我突然有一丝灵感,所以就写下我的想法,希望大家可以提出问题和改进,感谢。
我们知道Graham扫描法实现凸包是先找到纵坐标最小的点,基于这个想法我做了一些改进,也就是说Graham扫描法只是从一个方向发散出去,我们为何不从四个方向同时发散。也即找到x,y轴坐标最大或最小的四个点(也有可能是两个或者三个,这个先不讨论),以这四个点为基点,平面内的所有点必然都在由这四个点延申构成的矩形内
如左图,红色点就是我们所选择的基点,右图就是做完延伸线之后所形成的矩形,可以很简单的证明所有的点都在该矩形内。
接下来,连接这4个基点,可以形成四条线,如下图(为了方便讲述,我把D点平移了一下)
接下来我们就可以向外拓展了,以向右上方扫描为例,以AB为基线向右上方扫描,找到距离AB最远的一个点D,连接AD,DB,再以这两条线为基线,继续向外扫描,直到直线的外侧没有点为止,此时即构成凸包的一侧的边。
第一次:
第二次:
如图,凸包的右上边已经找出来了 :)
其他三个方向也是如上步骤。
接下来就是具体的实现
struct coordinate{
float x;
float y;
};
首先定义结构体coordinate存放点的坐标
float equation(coordinate s1,coordinate s2,coordinate s3){
float k=(s1.y-s2.y)/(s1.x-s2.x);
float c=(s1.y*s2.x-s2.y*s1.x)/(s2.x-s1.x);
float result=abs(k*s3.x-s3.y);
return result;
}
计算点s3到由s1,s2组成的直线的距离( 上面的result返回的不是真正的距离,因为+c和除以根号下k^2+1都是常数运算,对大小没有影响)
我这里对扫描的实现是将所有的点遍历一遍,然后得到一个小范围内的点,再计算这个范围内点的叉乘,以此来判断这个点是不是我们需要的最远点,代码如下(这里我只能想出这一个办法,如果大家有其他想法的话欢迎在评论下分享)
void findNE(coordinate s1,coordinate s2){
float x_min,x_max,y_min,y_max;
if(s1.x<s2.x){
x_min=s1.x;
y_max=s1.y;
x_max=s2.x;
y_min=s2.y;
}else{
x_min=s2.x;
y_max=s2.y;
x_max=s1.x;
y_min=s1.y;
}
int count=0;
for(int i=0;i<MAX;i++){
if(point[i].x>x_min&&point[i].x<x_max&&point[i].y>y_min&&point[i].y<y_max){
if(s1.x*s2.y+point[i].x*s1.y+s2.x*point[i].y-point[i].x*s2.y-s2.x*s1.y-s1.x*point[i].y<0){
//计算叉乘,>0在左边,<0在右边
dataNE[count]=point[i];
count++;
}
}
}
if(count==0){
cout<<"右上边已完成"<<endl;
}else{
int max_d=0;
for(int i=0;i<count;i++){
//dataNE是coordinate类型的数组,dis_data是用来存放距离的float型数组
dis_data[i]=equation(s1,s2,dataNE[i]);
if(dis_data[i]>max_d){
max_d=dis_data[i];
numb_NE=i;
}
}
finalData[index]=dataNE[numb_NE];
//finalData是coordinate型的数组,用来存放最终的凸包边界点的坐标
index++;
findNE(s1,dataNE[numb_NE]);
findNE(dataNE[numb_NE],s2);
}
}
-----------------------------------
我突然发现这个跟分治法有点像,但是还是有点参考价值吧 T_T