

### 587. Erect the Fence


题目:
<https://leetcode.com/problems/Erect-the-Fence/>


难度:

Hard



思路

题目要求用一个围栏把所有的点（🌲）围起来，然后求处于围栏上点（🌲）的集合。

我们可以发现，从最左边的那个点一直往右走，只要一直都是走的逆时针方向，那么我们一定可以找到这条围栏。那么接下来就考虑最简单的情况，

- 只有两个点```p```和```q```，我们从```p```走到```q```，当```p```到原点这条直线的斜率小于```q```到原点这条直线的斜率时，```p->q```就是沿逆时针方向走的；
- 接下来考虑3个点:```p，q，r```，以```p```为参照点（即前面的原点），那么从```q```走到```r```的时候，只要```q```到```q```这条直线的斜率小于```r```到```p```这条直线的斜率，```q->r```就是沿逆时针方向走的。

因此，我们只要构建一个```orientation```函数，就可以判断出目前我们的围栏是不是沿着逆时针在走下去了。

我们用一个```stack```来存放目前认为在围栏上的点的集合，然后把所有的点按照指定规则排好序：```先按照点的x坐标升序排列，如果x相等则按照点的y坐标升序排列```。这样我们依次取点，只要```stack```里面的点大于等于```2```个我们就要无限进行判断是否走的是逆时针，如果不是就把```stack```里面最后那个点```pop```出去（可能一直```pop```到只剩一个点），否则就把目前的这个点加入到```stack```中去，因为目前它还是在逆时针方向上的。

从左往右走完一遍```points```之后，我们围栏的下部分```lower hull```就构建好了，此时我们还要构建围栏的```upper hull```，因此我们将```points```逆序一下，从右往左再来一次遍历，仍然看是否走的是逆时针。但是这次遍历我们需要进行一个判断，就是之前放进```stack```的点，此时我们还是会经过它，如果它已经在```stack```里面了，我们就不需要再加进去了，同时这样也避免了我们把最左边的点重复加进去。



```python
python
# import functools
class Solution:
    def outerTrees(self, points):
        """
        :type points: List[Point]
        :rtype: List[Point]
        """
        def orientation(p, q, r):
            return (q.y - p.y)*(r.x - p.x) - (r.y - p.y)*(q.x - p.x)
        # def myComparator(p,q):
        #     return p.x - q.x if p.x != q.x else p.y - q.y
        stack= []
        # points.sort(key = functools.cmp_to_key(myComparator))
        points.sort(key = lambda p: (p.x, p.y))
        for i in range(len(points)):
            while (len(stack) >= 2 and orientation(stack[-2],stack[-1],points[i]) > 0):
                stack.pop()
            stack.append(points[i])
        points.reverse();
        for i in range(len(points)):
            while (len(stack) >= 2 and orientation(stack[-2],stack[-1],points[i]) > 0):
                stack.pop()
            if points[i] not in stack:
                stack.append(points[i])
        return stack
```
简化python版本
```python
class Solution(object):
    def outerTrees(self, points):
        """
        :type points: List[Point]
        :rtype: List[Point]
        """
        def orientation(p, q, r):
            return (q.y - p.y) * (r.x - q.x) - \
                   (q.x - p.x) * (r.y - q.y)

        hull = []
        points.sort(key=lambda p: (p.x, p.y))

        for i in itertools.chain(xrange(len(points)), \
                                 reversed(xrange(len(points)))):
            while len(hull) >= 2 and \
                  orientation(hull[-2], hull[-1],  points[i]) > 0:
                hull.pop()
            hull.append(points[i])

        return list(set(hull))
```

下面是小傅大神的代码，本来想叫‘’傅神‘’的，结果这名字🤦‍♂️（手动捂脸）[小傅每日一题587](https://www.bilibili.com/video/av15446980/)

另外其中的```stack.pop()```这行代码注释掉也是可以的

```java
java
class Solution {
    public List<Point> outerTrees(Point[] points) {
        List<Point> res = new ArrayList<Point>();
        Arrays.sort(points, new Comparator<Point>(){
            @Override
            public int compare(Point p, Point q){
                return p.x == q.x ? p.y - q.y : p.x - q.x;
            }
        });
        Stack<Point> stack = new Stack<>();
        for (int i = 0; i < points.length; i++){
            while(stack.size() >= 2 && orientation(stack.get(stack.size() - 2), stack.peek(), points[i]) > 0){
                stack.pop();
            }
            stack.push(points[i]);
        }
        //stack.pop();
        for (int i = points.length - 1; i >= 0; i--){
            while(stack.size() >= 2 && orientation(stack.get(stack.size() - 2), stack.peek(), points[i]) > 0){
                stack.pop();
            }
            stack.push(points[i]);
        }
        res.addAll(new HashSet<>(stack));
        return res;
    }
    
    public int orientation(Point p, Point q, Point r){
        return (q.y - p.y)*(r.x - p.x) - (r.y - p.y)*(q.x - p.x);
    }
}
```





Author: Keqi Huang

If you like it, please spread your support

![Support](https://github.com/Lisanaaa/myTODOs/blob/master/WechatIMG17.jpeg)
