Hello, reader ๐๐ฝ ! Welcome to day 49 of the series on Problem Solving. Through this series, I aim to pick up at least one question everyday and share my approach for solving it.
Today, I will be picking up LeetCode's daily challenge problem: 587. Erect the Fence.
๐ค Problem Statement - Monotone Chain
- There is an array trees where
trees[i] = [xi, yi]
represents the location of a tree in the garden. - The garden has to be fenced using the minimum length of rope as it is expensive. The garden is well fenced only if all the trees are enclosed.
Return the coordinates of trees that are exactly located on the fence perimeter.
E.g.:
points = [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]]
=>[[1,1],[2,0],[3,3],[2,4],[4,2]]
๐ฌ Thought Process - Montone Chain
- Honestly, I didn't solve this question on my own at all. Because I knew right away that it involves fancy master's level math that's I am not aware of.
- The idea of the approach is to sort all the tree coordinates by their x indices. If the x indices are same, then they are sorted by their y indices. This is because we are trying to find the farthest left and right points.
- Coordinates of the tree forms a convex hull which is divided into two parts: the upper and lower hull.
The fence coordinates for both the hulls can be found separately.
For your reference, given a set of points in a 2D plane, convex hull is the smallest polygon that contains all points within it.
Before calculating the fence coordinates, if the array only contains one tree coordinate, then we simply return the input.
- It is also worth noting that in a convex hull the adjacent point's edges have a degree less than 180.
- Hence we will try figuring out the angles between the edges of points using the formula to find the slope between two points.
To do this, we can use basic algebra to find the angle between adjacent edges formed by points.
Noice how we require three points to figure out angles between adjacent edges.
- Also in the image
m23 > m12
. So, sort the list of points to ensure that we find the angles between edges are increasing. We try to find angles that go in the clock wise direction. - But this could not be the case always. There could be a case when the slope of
[x1,y1]
and[x2,y2]
could be greater than that between[x3,y3]
and[x2,y2]
and hence the angle becomes > 180. - To handle both the cases we will break the convex into two parts as already stated.
- The coordinates in the upper half of the hull have angles between edges that go in the anti-clockwise direction. And the coordinates in the lower half of the hull have angles that go in the clockwise direction.
- We will calculate the points separately such that all the points whose edges form angles that are no longer than 180 degree.
- If any edge in the upper hull, the angle forms in a clockwise direction, it is ignored. Similarly for any edge in the lower hull forms an angle in the anti-clockwise direction, it is ignored.
- For the lower hull, we add the first two points to a stack. Then we iterate over the remaining points checking if it forms an angle in the anti-clockwise direction with the previous two points. If it does form, we consider it for the fence and push it onto the stack. If not, we ignore it.
- For the upper hull instead of finding angles in the clockwise direction, we start looking in the reverse order. Hence the anti-clockwise angles becomes angles in clockwise direction and vice versa.
- Hence the logic remains same and we will look for points again in anti-clockwise direction.
- Finally we iterate over all the valid points and remove the duplicates. Then we return the points.
๐ฉ๐ฝโ๐ป Solution - Monotone Chain
- Below is the code for the above discussed approach.
```
class Point {
int x, y;
Point(int x, int y) {
} }this.x = x; this.y = y;
class Solution { public int[][] outerTrees(int[][] trees) { if(trees.length == 1) return trees;
List<Point> points = new ArrayList<>();
for(int[] tree: trees) {
Point p = new Point(tree[0], tree[1]);
points.add(p);
}
int n = points.size();
Collections.sort(points, (a,b) -> a.x == b.x ? a.y - b.y : a.x - b.x);
Set<List<Integer>> duplicates = new HashSet();
Stack<Point> hull = new Stack();
hull.push(points.get(0));
hull.push(points.get(1));
for(int i = 2; i<n; i++) {
Point top = hull.pop();
while(!hull.isEmpty() && checkAngles(hull.peek(), top, points.get(i)) < 0) {
top = hull.pop();
}
hull.push(top);
hull.push(points.get(i));
}
for(int i = n-2; i>=0; i--) {
Point top = hull.pop();
while(!hull.isEmpty() && checkAngles(hull.peek(), top, points.get(i)) < 0) {
top = hull.pop();
}
hull.push(top);
hull.push(points.get(i));
}
List<Point> answer = new ArrayList();
int index = 0;
for(Point p: hull) {
ArrayList<Integer> temp = new ArrayList();
temp.add(p.x);
temp.add(p.y);
if(duplicates.contains(temp)) continue;
duplicates.add(temp);
answer.add(p);
}
int[][] ans = new int[answer.size()][2];
int i = 0;
for(Point p: answer) {
ans[i][0] = p.x;
ans[i][1] = p.y;
i++;
}
return ans;
}
private int checkAngles(Point p, Point q, Point r) {
return (q.x - p.x) * (r.y - p.y) - (q.y - p.y)* (r.x - p.x);
}
}
Time Complexity: O(n log n)
- n = number of coorindates
Space Complexity: O(n)
- n = number of coordinates
```
You can find the link to the GitHub repo for this question here: 587. Erect the Fence.
Conclusion
That's a wrap for today's problem. If you liked my explanation then please do drop a like/ comment. Also, please correct me if I've made any mistakes or if you want me to improve something!
Thank you for reading!