Convex Hull¶
The convex hull is the smallest convex polygon that contains all given points. It's a fundamental problem in computational geometry with applications in optimization, computer graphics, and spatial analysis.
What is a Convex Hull?¶
A convex hull of a set of points is the smallest convex polygon that contains all the points. A polygon is convex if any line segment joining two points inside the polygon lies entirely within the polygon.
Basic Concepts¶
Point Orientation¶
#include <vector>
#include <algorithm>
using namespace std;
struct Point {
double x, y;
Point(double x, double y) : x(x), y(y) {}
bool operator<(const Point& other) const {
if (x != other.x) return x < other.x;
return y < other.y;
}
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
};
// Orientation of three points
// Returns: 0 = collinear, 1 = counterclockwise, -1 = clockwise
int orientation(Point p, Point q, Point r) {
double val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
if (abs(val) < 1e-9) return 0; // Collinear
return (val > 0) ? 1 : -1; // Counterclockwise or clockwise
}
// Distance between two points
double distance(Point p, Point q) {
double dx = p.x - q.x;
double dy = p.y - q.y;
return sqrt(dx * dx + dy * dy);
}
Graham Scan Algorithm¶
Problem: Find the convex hull of a set of points using Graham Scan algorithm.
Sample Input:
- Points: [(0,0), (1,1), (2,2), (3,1), (2,0), (1,2)]
Sample Output:
- Convex hull: [(0,0), (2,0), (3,1), (2,2), (1,2)]
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;
class GrahamScan {
public:
vector<Point> convexHull(vector<Point>& points) {
int n = points.size();
if (n < 3) return points;
// Find the bottom-most point (and leftmost in case of tie)
int bottom = 0;
for (int i = 1; i < n; i++) {
if (points[i].y < points[bottom].y ||
(points[i].y == points[bottom].y && points[i].x < points[bottom].x)) {
bottom = i;
}
}
// Swap bottom point with first point
swap(points[0], points[bottom]);
// Sort points by polar angle with respect to bottom point
Point p0 = points[0];
sort(points.begin() + 1, points.end(), [p0](const Point& a, const Point& b) {
int o = orientation(p0, a, b);
if (o == 0) {
return distance(p0, a) < distance(p0, b);
}
return o == 1;
});
// Remove collinear points (keep only the farthest)
vector<Point> filtered;
filtered.push_back(points[0]);
for (int i = 1; i < n; i++) {
while (i < n - 1 && orientation(p0, points[i], points[i + 1]) == 0) {
i++;
}
filtered.push_back(points[i]);
}
if (filtered.size() < 3) return filtered;
// Build convex hull
stack<Point> hull;
hull.push(filtered[0]);
hull.push(filtered[1]);
hull.push(filtered[2]);
for (int i = 3; i < filtered.size(); i++) {
while (hull.size() > 1) {
Point top = hull.top();
hull.pop();
Point nextToTop = hull.top();
if (orientation(nextToTop, top, filtered[i]) == 1) {
hull.push(top);
break;
}
}
hull.push(filtered[i]);
}
// Convert stack to vector
vector<Point> result;
while (!hull.empty()) {
result.push_back(hull.top());
hull.pop();
}
reverse(result.begin(), result.end());
return result;
}
};
Andrew's Monotone Chain Algorithm¶
Problem: Find the convex hull using Andrew's Monotone Chain algorithm (alternative to Graham Scan).
Sample Input:
- Points: [(0,0), (1,1), (2,2), (3,1), (2,0), (1,2)]
Sample Output:
- Convex hull: [(0,0), (2,0), (3,1), (2,2), (1,2)]
#include <vector>
#include <algorithm>
using namespace std;
class MonotoneChain {
public:
vector<Point> convexHull(vector<Point>& points) {
int n = points.size();
if (n < 3) return points;
// Sort points by x-coordinate (and y-coordinate as tiebreaker)
sort(points.begin(), points.end());
// Build lower hull
vector<Point> lower;
for (int i = 0; i < n; i++) {
while (lower.size() >= 2 &&
orientation(lower[lower.size() - 2], lower[lower.size() - 1], points[i]) <= 0) {
lower.pop_back();
}
lower.push_back(points[i]);
}
// Build upper hull
vector<Point> upper;
for (int i = n - 1; i >= 0; i--) {
while (upper.size() >= 2 &&
orientation(upper[upper.size() - 2], upper[upper.size() - 1], points[i]) <= 0) {
upper.pop_back();
}
upper.push_back(points[i]);
}
// Remove duplicate points
lower.pop_back();
upper.pop_back();
// Combine lower and upper hulls
lower.insert(lower.end(), upper.begin(), upper.end());
return lower;
}
};
Advanced Applications¶
Convex Hull of Circles¶
Problem: Find the convex hull of circles (approximated as polygons).
Sample Input:
- Circles: [(0,0,1), (2,0,1), (1,2,1)] (center_x, center_y, radius)
Sample Output: - Convex hull vertices of the union of circles
#include <vector>
#include <cmath>
using namespace std;
struct Circle {
double x, y, radius;
Circle(double x, double y, double r) : x(x), y(y), radius(r) {}
};
class CircleConvexHull {
public:
vector<Point> convexHull(vector<Circle>& circles) {
vector<Point> points;
// Generate points on circle boundaries
for (const Circle& circle : circles) {
int numPoints = 16; // Number of points per circle
for (int i = 0; i < numPoints; i++) {
double angle = 2 * M_PI * i / numPoints;
double x = circle.x + circle.radius * cos(angle);
double y = circle.y + circle.radius * sin(angle);
points.push_back(Point(x, y));
}
}
// Find convex hull of all points
MonotoneChain chain;
return chain.convexHull(points);
}
};
Convex Hull with Collinear Points¶
Problem: Handle collinear points properly in convex hull computation.
Sample Input:
- Points: [(0,0), (1,1), (2,2), (3,3), (1,0), (2,0)]
Sample Output:
- Convex hull: [(0,0), (3,3), (2,0), (1,0)]
class ConvexHullWithCollinear {
public:
vector<Point> convexHull(vector<Point>& points) {
int n = points.size();
if (n < 3) return points;
// Find bottom-most point
int bottom = 0;
for (int i = 1; i < n; i++) {
if (points[i].y < points[bottom].y ||
(points[i].y == points[bottom].y && points[i].x < points[bottom].x)) {
bottom = i;
}
}
swap(points[0], points[bottom]);
Point p0 = points[0];
// Sort by polar angle
sort(points.begin() + 1, points.end(), [p0](const Point& a, const Point& b) {
int o = orientation(p0, a, b);
if (o == 0) {
return distance(p0, a) < distance(p0, b);
}
return o == 1;
});
// Remove collinear points (keep only the farthest)
vector<Point> filtered;
filtered.push_back(points[0]);
for (int i = 1; i < n; i++) {
while (i < n - 1 && orientation(p0, points[i], points[i + 1]) == 0) {
i++;
}
filtered.push_back(points[i]);
}
if (filtered.size() < 3) return filtered;
// Build hull
vector<Point> hull;
hull.push_back(filtered[0]);
hull.push_back(filtered[1]);
for (int i = 2; i < filtered.size(); i++) {
while (hull.size() > 1 &&
orientation(hull[hull.size() - 2], hull[hull.size() - 1], filtered[i]) != 1) {
hull.pop_back();
}
hull.push_back(filtered[i]);
}
return hull;
}
};
Convex Hull Area and Perimeter¶
Problem: Calculate the area and perimeter of a convex hull.
Sample Input:
- Points: [(0,0), (2,0), (2,2), (0,2)]
Sample Output:
- Area: 4
- Perimeter: 8
class ConvexHullMetrics {
public:
double calculateArea(vector<Point>& hull) {
if (hull.size() < 3) return 0;
double area = 0;
int n = hull.size();
for (int i = 0; i < n; i++) {
int j = (i + 1) % n;
area += hull[i].x * hull[j].y - hull[j].x * hull[i].y;
}
return abs(area) / 2.0;
}
double calculatePerimeter(vector<Point>& hull) {
if (hull.size() < 2) return 0;
double perimeter = 0;
int n = hull.size();
for (int i = 0; i < n; i++) {
int j = (i + 1) % n;
perimeter += distance(hull[i], hull[j]);
}
return perimeter;
}
};
Use Cases¶
Computational Geometry¶
- Point Set Analysis: Analyzing properties of point sets
- Polygon Operations: Intersection, union, and difference of polygons
- Voronoi Diagrams: Building Voronoi diagrams
- Delaunay Triangulation: Creating Delaunay triangulations
Computer Graphics¶
- Collision Detection: Efficient collision detection between objects
- Rendering: Optimizing rendering of complex shapes
- Shape Analysis: Analyzing and comparing shapes
- Mesh Generation: Generating meshes for 3D objects
Optimization¶
- Linear Programming: Finding feasible regions
- Resource Allocation: Optimizing resource distribution
- Facility Location: Finding optimal facility locations
- Path Planning: Planning optimal paths around obstacles
Spatial Analysis¶
- GIS Applications: Geographic information systems
- Map Analysis: Analyzing geographic data
- Terrain Analysis: Analyzing terrain features
- Urban Planning: Planning urban development
Complexity Analysis¶
Time Complexity¶
- Graham Scan: O(n log n) due to sorting
- Monotone Chain: O(n log n) due to sorting
- Gift Wrapping: O(nh) where h is number of hull points
- Quick Hull: O(n log n) average case, O(n²) worst case
Space Complexity¶
- All Algorithms: O(n) for storing points and hull
- Graham Scan: O(n) for stack
- Monotone Chain: O(n) for lower and upper hulls
Algorithm Comparison¶
| Algorithm | Time Complexity | Space Complexity | Advantages | Disadvantages |
|---|---|---|---|---|
| Graham Scan | O(n log n) | O(n) | Simple, stable | Requires sorting |
| Monotone Chain | O(n log n) | O(n) | No trigonometric functions | More complex |
| Gift Wrapping | O(nh) | O(n) | Simple, no sorting | Slow for many points |
| Quick Hull | O(n log n) avg | O(n) | Fast average case | O(n²) worst case |
Optimization Tips¶
- Choose Right Algorithm: Use Graham Scan for general cases, Monotone Chain for simplicity
- Handle Collinear Points: Remove or handle collinear points properly
- Coordinate Precision: Use appropriate precision for floating-point calculations
- Early Termination: Stop when hull is complete
- Memory Management: Use appropriate data structures for your use case
- Parallel Processing: Process independent parts in parallel when possible