题目描述
LeetCode: 587.安装栅栏
在一个二维的花园中,有一些用 (x, y) 坐标表示的树。由于安装费用十分昂贵,你的任务是先用最短的绳子围起所有的树。只有当所有的树都被绳子包围时,花园才能围好栅栏。你需要找到正好位于栅栏边界上的树的坐标。
示例:
1 | input: [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]] |
Andrew算法
算法思想
Andrew算法用于解决二维凸包问题。即在二维平面上用最小的周长包围所有点。因为周长最小,边界一定是凸多边形。所以从一个点出发,逆时针方向,相邻两条边夹角一定小于等于180°(向量夹角)。若大于,则一定不是边界的点,可以通过这样的方式来判断点是否在边界上。
Andrew算法将凸包分为两部分,以边界最左和最右的两个节点的连线为分界线,分为上壳和下壳两部分。下壳从x坐标最小值顺时针“拐”到x最大值;上壳从最大值拐到最小值。
算法步骤
- 根据x坐标升序排序,x相同则y升序排序
- 用栈维护凸包(边界)上的点。
- 计算下壳:从左到右遍历节点,对每一个新节点,与栈中最后一个节点组成一条边,栈中最后两个节点组成一条边,判断新节点是否在凸包上
- 如果在凸包上,入栈,进入下一个节点
- 如果不在凸包上,循环出栈,直至新节点与栈中最后一个节点组成的边在凸包上。
- 如果栈中少于两个点,则节点直接入栈
- 同理,从右到左遍历,计算上壳。
是否在凸包上的判断
用两个边的夹角(向量极角)来判断轨迹顺/逆时针旋转,即是否在凸包上。对于向量$\vec{pq}$,$\vec{qr}$,向量的叉积cross(p,q,r) =$\vec{pq}$×$\vec{qr}$,如果叉积小于 0,可以知道向量 $\vec{pq}$,$\vec{qr}$顺时针旋转,则此时向右拐;如果叉积大于 0,可以知道向量 $\vec{pq}$,$\vec{qr}$ 逆时针旋转,表示是左拐;如果叉积等于 0,则 $\vec{pq}$,$\vec{qr}$在同一条直线上。
1 | def cross(p: List[int], q: List[int], r: List[int]) -> int: |
代码实现
1 | class Solution: |