【软件构造】凸包算法------软件构造实验lab1

凸包的定义及算法

定义:

  • 在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,…Xn)的凸组合来构造。
  • 定义可能看不懂。说白了,凸包就是在已知的所有点中找到一个最少数目个点的集合,使得所有点都在这些点的连线形成的多边形内或多边形上。
  • 定义有两点需要注意:
    • 凸包之外的点位于多边形上也是符合条件的
    • 最小数目的点:如果形成的多边形有若干个相邻顶点在同一条直线上,则只保留距离最远的两个点

算法:

Graham扫描法:
  1. 在平面上一些散乱的点,首先 找找到这些点中处于最左下方的点
  2. 对这些点进行排序。把按照极角(polar angle)从小到大排序(以 p1为极点),极角相同的点按照到的距离从小到大排序
  3. 再开一个结构体数组s 来储存凸包最外围的点,也就是结果,这个有点容易让人搞迷。
  4. 遍历剩下的点,while循环把发现不是凸包顶点的点移除出去,因为当逆时针遍历凸包时,我们应该在每个顶点向左转。因此当while循环发现在一个顶点处没有向左转时,就把该顶点移除出去。
  • 此算法时间复杂度为(nlogn)
实际实验中:
  • 实际实验中并没有直接使用了这个算法,而是使用了一个简化了一些的算法。
  • 即对于每一个点,我们一次计算其余所有点到这个点的极角,并将极角最小的点作为凸包中当前点的下一个点。
  • 当有若干个点有相对于当前点有相同的极角时,取距离最远者。
  • 时间复杂度为O(n^2)
  • Java代码实现如下:
    /*
     * 主要思路为:
     * 先找到最角落的一个点,作为起始点(本函数中选用左上角的点)
     * 之后从这个点开始,依次向下找,从当前角度顺时针旋转遇到的第一个点即为下一个点,
     * 将找到的点加入结果set,并将其作为当前点,同时将从上一点到这一点需要旋转的角度作为当前角度。
     * 如果同一角度上有两个点,则选择与当前点距离较远的点作为下一个点。
     * 以此循环,直至下一个点为起始点,说明此时凸包上的点已经全部被找到。
     */
    public static Set<Point> convexHull(Set<Point> points) {


        double minBearing = 362;
        List<Point> pointArray = new ArrayList<Point>(points);
        Set<Point> result = new HashSet<Point>();
        if (pointArray.size() == 0) {
            return result;
        }

        Point firstPoint = pointArray.get(0);
        Point nextPoint = null;
        Point currentPoint = null;
        for (int i = 0; i < pointArray.size(); i++) {
            if ((pointArray.get(i).x() < firstPoint.x()) || (pointArray.get(i).x() == firstPoint.x() && pointArray.get(i).y() > firstPoint.y())) {
                firstPoint = pointArray.get(i);
            }
        }
        if (pointArray.size() == 1) {
            result.add(firstPoint);
            return result;
        } else {
            for (int i = 0; i < pointArray.size(); i++) {
                if (firstPoint == pointArray.get(i)) {
                    continue;
                }
                double tmp = calculateBearingToPoint(0, (int) firstPoint.x(), (int) firstPoint.y(), (int) pointArray.get(i).x(), (int) pointArray.get(i).y());
                if (minBearing > tmp) {
                    nextPoint = pointArray.get(i);
                    minBearing = tmp;
                }
            }
        }
        currentPoint = firstPoint;
        while (nextPoint != firstPoint) {
            double startBearing = calculateBearingToPoint(0,(int)currentPoint.x(),(int)currentPoint.y(),(int)nextPoint.x(),(int)nextPoint.y());
            result.add(nextPoint);
            currentPoint = nextPoint;
            minBearing = 361;
            for (int i = 0; i < pointArray.size(); i++) {
                if (currentPoint == pointArray.get(i)) continue;
                else {
                    double tmp = calculateBearingToPoint(startBearing, (int) currentPoint.x(), (int) currentPoint.y(), (int) pointArray.get(i).x(), (int) pointArray.get(i).y());
                    if (minBearing - tmp > 0.001) {
                        nextPoint = pointArray.get(i);
                        minBearing = tmp;
                    } else if (Math.abs(minBearing-tmp) < 0.001) {
                        double distanceNextPoint = (currentPoint.x() - nextPoint.x()) * (currentPoint.x() - nextPoint.x()) + (currentPoint.y() - nextPoint.y()) * (currentPoint.y() - nextPoint.y());
                        double distancePointArrayI = (currentPoint.x() - pointArray.get(i).x()) * (currentPoint.x() - pointArray.get(i).x()) + (currentPoint.y() - pointArray.get(i).y()) * (currentPoint.y() - pointArray.get(i).y());
                        if (distancePointArrayI > distanceNextPoint) {
                            nextPoint = pointArray.get(i);
                        }
                    }
                }
            }
        }
        result.add(firstPoint);

        return result;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值