二维凸包 Andrew算法

96 阅读1分钟

Problem: 587. 安装栅栏

参考:Andrew算法

二维凸包模板题

Code


class Solution {
    int[] subtraction(int[] a, int[] b) {
        // 向量相减
        return new int[]{a[0] - b[0], a[1] - b[1]};
    }

    double cross(int[] a, int[] b) {
        // 叉乘
        return a[0] * b[1] - a[1] * b[0];
    }

    double getArea(int[] a, int[] b, int[] c) {
        // ab -> ac 过程中向量扫过的面积
        return cross(subtraction(b, a), subtraction(c, a));
    }

    public int[][] outerTrees(int[][] trees) {
        Arrays.sort(trees, (a, b) -> {
            return a[0] != b[0] ? a[0] - b[0] : a[1] - b[1];
        });

        int n = trees.length;
        int tp = 0;
        int[] stk = new int[n + 10]; // 使用数组模拟栈,更便于获取栈顶两个元素
        boolean[] vis = new boolean[n + 10];

        stk[++tp] = 0; // 不标记起点,第二轮时还需要使用

        for (int i = 1; i < n; i++) {
            // 第一轮,遍历下壳
            int[] c = trees[i];
            while (tp >= 2) {
                // 至少需要两个点
                int[] a = trees[stk[tp - 2]];
                int[] b = trees[stk[tp - 1]];
                // ac 在 ab 外边,所以 b 出栈, c入栈
                if (getArea(a, b, c) < 0) vis[stk[--tp]] = false;
                else break;
            }

            stk[tp++] = i;
            vis[i] =true;
        }

        int size = tp;
        for (int i = n - 1; i >= 0; i--) {
            // 第二轮,遍历上壳
            if (vis[i]) continue;
            int[] c= trees[i];
            while (tp >= size + 1) {
                int[] a = trees[stk[tp - 2]];
                int[] b = trees[stk[tp - 1]];
                if (getArea(a, b, c) < 0) vis[stk[--tp]] = false;
                else break;
            }
            stk[tp++] = i;
            vis[i] = true;
        }

        int[][] ans = new int[tp-1][2];
        for (int i = 0; i < tp - 1; i++) {
            ans[i] = trees[stk[i]];
        }
        return ans;
    }
}