【Leetcode】587. Erect the Fence 587. 安装栅栏

12

解法

给点集求凸包的问题,有很多种算法
一个比较简单的算法是Monotone chain算法
它需要将点排序,在结点排好序的情况下是 O ( n ) O(n)

  1. 将点按x,y从小到大排序
  2. 求凸包下界lower,正向遍历所有节点,判断当前节点在栈顶两个节点构成的向量的左边还是右边
  3. 如果在左边,那么把当前节点入栈
  4. 如果在右边,那么pop栈顶的节点
  5. 重复步骤2,直到遍历完成

然后求凸包上界upper,它需要反向遍历所有节点,步骤和上述一样。

判断新节点在向量左边还是右边需要用到叉积的一些知识。

由于本题中,在一条直线上的3个点也要算到边界里,所以cross里的判断是>=0,也因此upperlower会有除了头尾之外的次,所以最后要去重

如果一条直线只算头尾的话,那么cross里应该是>0,并且最后返回lower[:-1]+upper[:-1]即可

# Definition for a point.
# class Point(object):
# def __init__(self, a=0, b=0):
# self.x = a
# self.y = b

class Solution(object):
    def outerTrees(self, points):
        """ :type points: List[Point] :rtype: List[Point] """
        n = len(points)
        if n<=2:
            return points
        points = sorted(points, key=lambda x:(x.x,x.y))
        def cross(i,j,k):
            x1,y1 = points[j].x-points[i].x,points[j].y-points[i].y
            x2,y2 = points[k].x-points[i].x,points[k].y-points[i].y
            return x1*y2-x2*y1>=0
        lower = []
        for i in xrange(n):
            while len(lower)>=2 and not cross(lower[-2],lower[-1],i):
                lower.pop()
            lower.append(i)
        upper = []
        for i in xrange(n-1,-1,-1):
            while len(upper)>=2 and not cross(upper[-2],upper[-1],i):
                upper.pop()
            upper.append(i)
        return map(lambda x:points[x],set(lower)|set(upper))