Java implementation LeetCode 587 installed fence (Figure algorithm into mathematical problems)

587. The installation of the fence

In a two-dimensional garden, there are a number of trees with (x, y) coordinates. As the installation costs are very expensive, your task is to use the shortest rope enclosing all the trees. Only when all the trees are surrounded by a rope around the garden to a good fence. You just need to find the coordinates of the tree is located on the border fence.

Example 1:

Input: [[1,1], [2,2], [2,0], [2,4], [3,3], [4,2]]
Output: [[1,1], [2 , 0], [4,2], [3,3], [2,4]]
explanation:
Here Insert Picture Description
example 2:

Input: [[1,2], [2,2], [4,2]]
Output: [[1,2], [2,2], [4,2]]
Explanation:
Here Insert Picture Description
even if the trees are in a straight line on, you need to surround them with a rope.

note:

All trees are to be enclosed together. You can not cut the string to surround the tree or into the tree more than one group.
Input integer between 0 and 100.
At least one tree garden.
Coordinate all the trees are different.
No point input sequence. Output order is also not required.

class Solution {
   public int[][] outerTrees(int[][] points) {
        Set<int[]> hull = new HashSet<>();

        // 如果树的棵数小于 4 ,那么直接返回
        if (points.length < 4) {
            for (int[] p : points) hull.add(p);
            return hull.toArray(new int[hull.size()][]);
        }

        // 找到最左边的点
        int leftMost = 0;
        for (int i = 0; i < points.length; i++) {
            if (points[i][0] < points[leftMost][0]) leftMost = i;
        }

        int p = leftMost;
        do {
            int q = (p + 1) % points.length;

            for (int i = 0; i < points.length; i++) {
                // 如果 i 点在 pq 线下方,则使用 i 点
                if (orientation(points[p], points[i], points[q]) < 0) q = i;
            } 
            for (int i = 0; i < points.length; i++) {
                // p、q、i 在同一条线上的情况,并且 i 在 p 和 q 的中间的时候
                // 也需要将这个点算进来
                if (i != p && i != q 
                    && orientation(points[p], points[i], points[q]) == 0 
                    && inBetween(points[p], points[i], points[q])) {
                    hull.add(points[i]);
                }
            }
        
            hull.add(points[q]);
            // 重置 p 为 q,接着下一轮的遍历
            p = q;
        } while (p != leftMost);

        return hull.toArray(new int[hull.size()][]);
    }

    // 以下 pq 和 qr 都是向量
    // pq * qr > 0 表示 r 点在 pq 线上方
    // pq * qr < 0 表示 r 点在 pq 线下方
    // pq * qr = 0 表示 p、q、r 一条线
    //           |(q[0]-p[0]) (q[1]-p[1])|
    // pq * qr = |                       | = (q[0]-p[0]) * (r[1]-q[1]) - (r[0]-q[0]) * (q[1]-p[1])
    //           |(r[0]-q[0]) (r[1]-q[1])|
    private int orientation(int[] p, int[] r, int[] q) {
        return (q[0] - p[0]) * (r[1] - q[1]) - (r[0] - q[0]) * (q[1] - p[1]);
    }

    // 判断 r 点是不是在 p 点和 q 点之间,需要考虑以下两种情况:
    // 1. q 点在 p 点的左边或者右边
    // 2. q 点在 p 点的上边或者下边
    private boolean inBetween(int[] p, int[] r, int[] q) {
        boolean a = r[0] >= p[0] && r[0] <= q[0] || r[0] <= p[0] && r[0] >= q[0];
        boolean b = r[1] >= p[1] && r[1] <= q[1] || r[1] <= p[1] && r[1] >= q[1];
        return a && b;
    }
}
Released 1660 original articles · won praise 20000 + · Views 3.1 million +

Guess you like

Origin blog.csdn.net/a1439775520/article/details/105176400