人类对于未知的事物,第一反应总是恐惧的。
大一在ACM做题的时候,遇到过一个凸包问题。然后看其他同学下了个模板,朦朦胧胧的给过了,就感觉这么难!网上都有模板了,自己看来是写不出这种模板的。
现在看了算法导论,想想当时的自己,已经哭晕在厕所。
Graham_Scan算法,主要是对于数学的叉积的理论的熟悉比较重要,向量p1p0 ( x1 , y1 ) 叉积 p2p0( x2 , y2 ) : ( x1 * y2 ) - ( y1 * x2 )。结果为正的,则表示p2p0向量在p1p0向量的左侧。
而在Graham_Scan算法里,因为要将所有的点都包起来,所以B点距离下一个点C,则B点之前的A点与C点构成的向量与B点与C点构成的向量的叉积肯定是要负的,如果是正的,则要将B点排除出凸包边上点集合。
public class Graham_Scan {
public void Scan(Node[] ns){
Node[] Q = new Node[ns.length];//保存凸包边上的点;
int top = 0;//保存 栈顶位置
int k = 0 ;
for(int i = 1 ; i < ns.length ; i ++)
if (ns[i].y < ns[k].y || (ns[i].y == ns[k].y && ns[i].x < ns[k].x))
k = i;
Node tmp = ns[k];
ns[k] = ns[0];
ns[0] = tmp;
for(int i = 1 ; i < ns.length ; i ++){
k = i;
for(int j = i + 1 ; j < ns.length ; j ++){
if ( (cross(ns[j], ns[k], ns[0]) > 0 ) || ( cross(ns[j], ns[k], ns[0]) == 0 && dist(ns[j], ns[0]) < dist(ns[k], ns[0]) ) ) {
k = j;
}
}
tmp = ns[k];
ns[k] = ns[i];
ns[i] = tmp;
}
top = -1;
Q[++top] = ns[0];
Q[++top] = ns[1];
Q[++top] = ns[2];
for(int i = 3; i < ns.length ; i ++){
while (cross(ns[i], Q[top], Q[top - 1]) > 0) {//
top --;
}
Q[++top] = ns[i];
}
}
private int cross(Node n1, Node n2, Node n0){
int sign = (n1.x - n0.x)*(n2.y - n0.y) - (n2.x - n0.x)*(n2.y - n0.y);
return sign;
}
private double dist(Node n1 , Node n2){
return Math.sqrt((n1.x - n2.x)*(n1.x - n2.x) + (n1.y - n2.y)*(n1.y - n2.y));
}
private class Node{
private int x;
private int y;
}
}