凸包问题就是给定一堆点,求包含所有点的最小多边形。
应用
板上钉钉子
机器人最短路径
机器人要绕过障碍物,到达另一个点,最短的路径是什么
最远节点问题
算法
算法的基本思想就是先找到y坐标最小的点,再计算它与其它点的所成的角度,将其他的点按照角度进行排序。
依次连接各个点,如果出现大于等于180度的角,就说明该点在多边形里面,不是顶点,因此跳过该点。
如何找出y坐标最小的点
依次扫描所有的点,记录y坐标最小的点,记为点p。
如何按角度进行排序
分别计算每个点到p与x坐标正方向的夹角,再对浮点数进行排序
如何判断p1 p2 p3角度小于180度
CCW:给定三个点a b c,判断a->b->c是否为逆时针旋转
2×三角形的面积=叉乘的模=(bx-ax)(cy-ay)-(by-ay)(cx-ax)
如果面积大于0,则表示a->b->c为逆时针,面积小于0,则表示a->b->c为顺时针,如果面积等于0,则表示a b c共线
如何高效地排序
使用归并排序,复杂度为NlogN
代码
别看代码这么长,其实核心算法只有11行。
import
java.util.Arrays;
import
java.util.Stack;
public
class
ConvexHull {
public
static
Point2D[] vertex(Point2D[] li) {
// 复制坐标点
int
N = li.length;
Point2D[] li2 =
new
Point2D[N];
for
(
int
i=
0
;i<N;i++){
li2[i] = li[i];
}
// 找出y坐标最小的点
for
(
int
i=
1
;i<N;i++){
Point2D e=li[i];
boolean
swap =
false
;
if
(e.y<li[
0
].y){
swap=
true
;
}
else
if
(e.y==li[
0
].y && e.x > li[
0
].y){
swap=
true
;
}
if
(swap){
Point2D temp = li[
0
];
li[
0
] = li[i];
li[i] = temp;
}
}
Point2D minPoint = li[
0
];
// 计算每个顶点的角度
AnglePoint[] angles =
new
AnglePoint[N];
angles[
0
] =
new
AnglePoint(li[
0
],-
999
);
for
(
int
i=
1
;i<N;i++){
Point2D pt = li[i];
float
angle = getAngle(minPoint, pt);
angles[i] =
new
AnglePoint(pt, angle);
}
// 按照角度进行排序
Arrays.sort(angles);
// 凸包的核心算法
Stack<Point2D> result =
new
Stack<Point2D>();
result.push(angles[
0
].point);
result.push(angles[
1
].point);
for
(
int
i=
2
;i<N;i++){
Point2D top = result.pop();
Point2D pt = angles[i].point;
while
(!result.isEmpty() && ccw(result.peek(),top,pt) <=
0
){
//第一个点是result.peek()而不是minPoint。不要忘了isEmpty
top = result.pop();
}
result.push(top);
result.push(pt);
}
// 返回顶点的数组
return
result.toArray(
new
Point2D[
0
]);
}
public
static
float
ccw(Point2D a, Point2D b, Point2D c){
return
(b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
public
static
float
getAngle(Point2D a, Point2D b){
float
dx = b.x-a.x;
float
dy = b.y-a.y;
double
cos = (dx/Math.sqrt(dx*dx+dy*dy));
return
(
float
)Math.acos(cos);
}
}
class
Point2D {
public
float
x;
public
float
y;
public
Point2D(
float
x,
float
y){
this
.x=x;
this
.y=y;
}
}
class
AnglePoint
implements
Comparable<AnglePoint> {
public
Point2D point;
public
float
angle;
public
AnglePoint(Point2D point,
float
angle) {
this
.point = point;
this
.angle= angle;
}
@Override
public
int
compareTo(AnglePoint that){
if
(
this
.angle < that.angle){
return
-
1
;
}
else
if
(
this
.angle > that.angle) {
return
1
;
}
else
{
return
0
;
}
}
}