凸包 - 确定点的顺序

3

我正在学习凸包算法,并从朴素的暴力算法到 Graham Scan 的所有算法进行总结。

这是我朴素的 O(n^4) 算法。首先,假设所有点都是凸壳的一部分。对于每个可能的三角形,消除在三角形内部的所有点。最后,那些没有被消除的点将成为凸壳的一部分。

以下是 Java 代码(已修复:使用了 Thomash 的解决方案)

public List<Point> naive(List<Point> points) {
    if (points == null)
        return Collections.emptyList();
    if (points.size() <= 3)
        return points;
    boolean[] extremePoints = new boolean[points.size()];
    Arrays.fill(extremePoints, true);
    for (int i = 0, sz = points.size(); i < sz; i++) {
        if (extremePoints[i])
            for (int j = 0; j < sz; j++) {
                if (i != j && extremePoints[j]) {
                    for (int k = 0; k < sz; k++) {
                        if (k != i && k != j) {
                            for (int l = 0; l < sz; l++) {
                                if (extremePoints[l] && l != i && l != j
                                        && l != k) {
                                    // Check if P[l] lies in triangle formed
                                    // by
                                    // P[i],P[j],P[k]

                                    Polygon p = new Polygon();
                                    p.addPoint(points.get(i).x,
                                            points.get(i).y);
                                    p.addPoint(points.get(j).x,
                                            points.get(j).y);
                                    p.addPoint(points.get(k).x,
                                            points.get(k).y);
                                    if (p.contains(points.get(l)))
                                        extremePoints[l] = false;
                                }
                            }
                        }
                    }
                }
            }
    }

    Point centerOfHull = null; // Arbitrary point inside the hull
    // Order?
    for (int i = 0; i < extremePoints.length; i++) {
        if (!extremePoints[i]) {
            centerOfHull = points.get(i);
            break;
        }
    }
    List<Point> convexHull = new ArrayList<Point>();
    for (int i = 0; i < extremePoints.length; i++) {
        if (extremePoints[i]) {
            convexHull.add(points.get(i));
        }
    }
    Collections.sort(convexHull, new PointComp(centerOfHull));
    // or use a heap. still O(nlogn)
    return convexHull;
}

private class PointComp implements Comparator<Point> {

    private Point center;

    public PointComp(Point center) {
        this.center = center;
    }

    @Override
    public int compare(Point o1, Point o2) {
        double angle1 = Math.atan2(o1.y - center.y, o1.x - center.x);
        double angle2 = Math.atan2(o2.y - center.y, o2.x - center.x);
        if (angle1 < angle2)
            return 1;
        else if (angle2 > angle1)
            return -1;
        return 0;
    }
}

我尝试直观地看这些点,它们似乎是正确的,但是我不知道如何确定这些点的顺序来绘制凸包多边形?任何帮助都将不胜感激。

2个回答

2
如果你正在使用暴力破解来查找哪些点是凸包的一部分,那么你可能会继续做丑陋的事情来找到点的顺序。
1. 从左上角的点开始。 2. 计算该点到所有其他点的角度。 3. 选择角度最接近0度的点。这是凸包顺时针方向的下一个点。 4. 重复以上步骤。
当您沿着凸包旋转时,必须调整目标角度,但这很有效(并且这是您在纸上完成任务的一种方式)。

如果您这样做,无需先将属于外壳的点与其他点分开。 - Nicolas Repiquet
@NicolasRepiquet:确实如此,但是如果您已经拥有凸包上的点集,则这比从头开始做要快。 - Li-aung Yip
@Li-aungYip,虽然我理解你的方法,但我选择了Thomash的方法,因为它在实现上更简单。我已经给你们两个的解决方案点赞,但是我会接受他的。再次感谢! :) - st0le

1
在凸包内选择一个点O和另一个点A。对于凸包中的每个点B,计算角度AÔB,并使用这些角度对点进行排序(如果AÔB < AÔB',则我们认为B < B')。

你能解释一下吗?我没太明白“使用这些角度排序点”的意思。 - st0le
最终我使用了你的方法,使用了一个比较器来测量相对于原点的角度。我会编辑我的问题以包含代码。谢谢。 :) - st0le
有没有办法可以消除 Math.atan2,只使用斜率呢?我想这样会更快。:\ - st0le
@st0le:是的,你应该可以使用“斜率”来解决问题。 - Li-aung Yip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接