Erect the fence
November 19, 2022
math-and-geometryProblem URL: Erect the fence
The problem is asking us to find the convex hull given a set of 2D points.
Monotone Chain algorithm:
- Sort the points by x. (moving from left to right)
- Initialize a stack with the first 2 points.
- Starting from the thrid point (P3) in points , P1 = stack[-2] and P2 = stack[-1], if P3 is less counterclockwise to P1 than P2, pop P2 from the stack, keep doing this until P3 is more counterclockwise to P1 than P2 or if there is only one point left in the stack.
- Append the current point to the stack and go to the next one.
- Reverse points (moving from right to left), and repeat (2) - (4) to get the second Monotone Chain Stack.
What this algorithm does is, first going from left to right of the points, construct the lower hull (the bottom half of the hull). Then, going from right to left, construct the upper hull (top half of the hull).
The process (2)-(4) is very similar to construct a regular Monotonic Stack, but instead of mataining the stack monotonically increasing, we maintain the chain fromed by the points in the stack moving counterclockwise!
Now the problem is given 3 points (P1, P2, P3), how to figure out if the chain P1->P2->P3 is moving counterclockwise. This is done by doing a Cross Product of the two vectors formed by V1 = (P1,P2) and V2 = (P2,P3). If you forgot the meaning of cross product please take 2 minutes and watch this video HERE (Time: 00:40 to 02:20). So basically, if the cross product of V1 and V2 is positive, this chain is not moving counterclockwise (pop P2 from the stack). And if the cross product is negtive, this chain is moving counterclockwise (append P3 to the stack).
Cross product formula:
V1 = (a,b), V2 = (c,d)
V1 X V2 = ad - bc
class Solution:
def outerTrees(self, points: List[List[int]]) -> List[List[int]]:
def crossProduct(p1: List[List[int]], p2: List[List[int]], p3: List[List[int]]):
# V1 = (a,b), V2 = (c,d)
# V1 X V2 = a*d - b*c
a = p2[0]-p1[0]
b = p2[1]-p1[1]
c = p3[0]-p1[0]
d = p3[1]-p1[1]
return a*d - b*c
def constructHalfHull(points: List[List[int]]):
stack = []
for p in points:
### if the chain formed by the current point
### and the last two points in the stack is not counterclockwise, pop it
while len(stack)>=2 and crossProduct(stack[-2],stack[-1],p)>0:
stack.pop()
### append the current point.
stack.append(tuple(p))
return stack
points.sort()
leftToRight = constructHalfHull(points)
### reverse points, so we are moving from right to left
rightToLeft = constructHalfHull(points[::-1])
### it is posible that the top and bottom parts have same points (e.g., all points form a line)
### we remove the duplicated points using a set
return list(set(leftToRight + rightToLeft))
Time complexity: O(nlogn)
Space complexity: O(n)