Erect the Fence

Convex hull via Andrew's monotone chain — return all hull points including collinear edge points.

Hard
Time O(n log n) Space O(n)
LeetCode
6 min read
geometry convex-hull math

Problem#

Given trees as 2D points, return the trees on the convex fence (perimeter of the convex hull), including any collinear trees lying on the hull edges.

Examples#

  • [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]][[1,1],[2,0],[4,2],[3,3],[2,4]] (any order).

Constraints#

  • 1 <= trees.length <= 3000.

Approach#

Andrew’s monotone chain. Sort lexicographically. Build the lower hull, then the upper. For “include collinear” we use cross < 0 (strict) on the right-turn rejection so points on a line are kept; then deduplicate.

Solution#

Erect the Fence
class Solution {
public:
vector<vector<int>> outerTrees(vector<vector<int>>& trees) {
sort(trees.begin(), trees.end());
int n = trees.size();
if (n <= 2) return trees;
auto cross = [](vector<int>& O, vector<int>& A, vector<int>& B) {
return (long long)(A[0] - O[0]) * (B[1] - O[1])
- (long long)(A[1] - O[1]) * (B[0] - O[0]);
};
vector<vector<int>> hull;
// lower
for (auto& p : trees) {
while (hull.size() >= 2 && cross(hull[hull.size()-2], hull.back(), p) < 0) hull.pop_back();
hull.push_back(p);
}
int lower = hull.size() + 1;
// upper
for (int i = n - 2; i >= 0; --i) {
auto& p = trees[i];
while ((int)hull.size() >= lower && cross(hull[hull.size()-2], hull.back(), p) < 0) hull.pop_back();
hull.push_back(p);
}
hull.pop_back();
sort(hull.begin(), hull.end());
hull.erase(unique(hull.begin(), hull.end()), hull.end());
return hull;
}
};

Editorial#

Strict < 0 keeps collinear points (those with cross product 0 stay on the hull) — the standard <= 0 would prune them. The two halves share endpoints; popping the last and dedup’ing at the end is simpler than tracking the lower-half size carefully.

Complexity#

  • Time: O(n log n).
  • Space: O(n).

Concept revision#

Search ESC

Keyboard shortcuts

Shortcuts are disabled while typing in inputs.