凸包问题 java_凸包问题:Graham's Scan解法

本文介绍了计算几何中的凸包概念,即给定二维平面点集,凸包是连接最外层点构成的能包含所有点的凸多边形。还阐述了求解凸包顶点的Grahanm's Scan算法思路,并给出了Java代码实现,通过栈来保存凸包上的点。

概念

凸包(Convex Hull)是一个计算几何(图形学)中的概念。用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边型,它能包含点集中所有点的。

问题

给定平面的点集,求出凸包的所有顶点。

算法思路

Grahanm's Scan算法的主要思路如下:

1,选择P中y坐标最小的点为起始点p0,若有多个这样的点则进一步选取其中x坐标最小的点为p0;

2,设是P中剩余的点,对其按逆时针方向相对p0的极角进行从小到大排序,若有多个点有相同的极角,则离p0点近的排在前面;

3,设排序后的点的顺序为p1,p2,……,pm,分别计算向量p0p1与p1p2的叉积,二维矢量的叉积a × b = IaI•IbIsinθ,θ为从a到b的夹角,如果b在a的逆时针方向,则sinθ为正,叉积也就为正,在我们逆时针遍历的情况下,如果b在a的顺时针方向,则p1点必定不是凸包上的点,反之则是在凸包上的点。所以我们使用栈来保存凸包上的点,遍历过程中发现栈顶的点不是凸包上的点就pop出来,如果是就继续把接下来的点放入栈中。

图片可能更容易理解,这里就放几张别人博客的图片:

b76cd886c10f

Grahanm's Scan

JAVA实现

import java.util.*;

public class Main {

static class Point {

int x;

int y;

public Point(int x, int y) {

this.x = x;

this.y = y;

}

@Override

public String toString() {

return "Point{" +

"x=" + x +

", y=" + y +

'}';

}

}

// 用于存储起始点

public static Point centerPoint;

// 叉积

public static int CJ(int x1, int y1, int x2, int y2){

return x1*y2 - x2*y1;

}

// 计算向量(p1->p2)和(p2->p3)两个向量的叉积

public static int compare(Point p1, Point p2, Point p3){

return CJ(p2.x-p1.x, p2.y - p1.y, p3.x-p2.x, p3.y-p2.y);

}

public static void main(String arg[]){

Scanner in = new Scanner(System.in);

int n = in.nextInt();

List points = new ArrayList<>();

for (int i=0; i

int x = in.nextInt();

int y = in.nextInt();

Point point = new Point(x, y);

// 找到x,y都最小的点作为起始点

if (centerPoint == null || centerPoint.y>point.y || (centerPoint.y==point.y && centerPoint.x>point.x)){

centerPoint = point;

}

points.add(point);

}

// 把起始点拿出去

points.remove(centerPoint);

// 按照与起始点向量的极角排序

Collections.sort(points, (p1, p2) -> {

if (Math.atan2(p1.y- centerPoint.y, p1.x- centerPoint.x) != Math.atan2(p2.y- centerPoint.y, p2.x- centerPoint.x)){

// atan2可以计算极角

return Math.atan2(p1.y- centerPoint.y, p1.x- centerPoint.x) < Math.atan2(p2.y- centerPoint.y, p2.x- centerPoint.x)? -1 : 1;

}

// 极角相同与中心点距离越近,则x坐标也就越近

return Math.abs(p1.x- centerPoint.x) - Math.abs(p2.x- centerPoint.x);

});

// 用数组模拟栈

Point[] stack = new Point[n];

stack[0] = centerPoint;

// 栈顶指针

int top = 0;

// 遍历点集中每一个点

for (int i=0; i

//如果栈中元素大于等于2并且(p2->p3)在(p1->p2)的顺时针方向,栈顶的点抛出

while (top>=1&&compare(stack[top-1], stack[top], points.get(i)) < 0) top--;

// 找到在逆时针方向的点了,将p2放入栈顶

stack[++top] = points.get(i++);

}

// 输出栈中的所有点就是凸包上的点

for (int i=0; i<=top; i++){

System.out.println(stack[i]);

}

}

}

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值