Open In App

Convex Hull | Monotone chain algorithm

Last Updated : 11 Apr, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given a set of points, the task is to find the convex hull of the given points. The convex hull is the smallest convex polygon that contains all the points. 
Please check this article first: Convex Hull | Set 1 (Jarvis’s Algorithm or Wrapping) 
 

Examples:

Input: Points[] = {{0, 3}, {2, 2}, {1, 1}, {2, 1}, {3, 0}, {0, 0}, {3, 3}} 
Output: 
(0, 0) 
(3, 0) 
(3, 3) 
(0, 3) 
 

 

Approach: Monotone chain algorithm constructs the convex hull in O(n * log(n)) time. We have to sort the points first and then calculate the upper and lower hulls in O(n) time. The points will be sorted with respect to x-coordinates (with respect to y-coordinates in case of a tie in x-coordinates), we will then find the left most point and then try to rotate in clockwise direction and find the next point and then repeat the step until we reach the rightmost point and then again rotate in the clockwise direction and find the lower hull.
Below is the implementation of the above approach:
 

CPP




// C++ implementation of the approach
#include <bits/stdc++.h>
#define llu long long int
using namespace std;
 
struct Point {
 
    llu x, y;
 
    bool operator<(Point p)
    {
        return x < p.x || (x == p.x && y < p.y);
    }
};
 
// Cross product of two vectors OA and OB
// returns positive for counter clockwise
// turn and negative for clockwise turn
llu cross_product(Point O, Point A, Point B)
{
    return (A.x - O.x) * (B.y - O.y)
           - (A.y - O.y) * (B.x - O.x);
}
 
// Returns a list of points on the convex hull
// in counter-clockwise order
vector<Point> convex_hull(vector<Point> A)
{
    int n = A.size(), k = 0;
 
    if (n <= 3)
        return A;
 
    vector<Point> ans(2 * n);
 
    // Sort points lexicographically
    sort(A.begin(), A.end());
 
    // Build lower hull
    for (int i = 0; i < n; ++i) {
 
        // If the point at K-1 position is not a part
        // of hull as vector from ans[k-2] to ans[k-1]
        // and ans[k-2] to A[i] has a clockwise turn
        while (
            k >= 2
            && cross_product(ans[k - 2], ans[k - 1], A[i])
                   <= 0)
            k--;
        ans[k++] = A[i];
    }
 
    // Build upper hull
    for (size_t i = n - 1, t = k + 1; i > 0; --i) {
 
        // If the point at K-1 position is not a part
        // of hull as vector from ans[k-2] to ans[k-1]
        // and ans[k-2] to A[i] has a clockwise turn
        while (k >= t
               && cross_product(ans[k - 2], ans[k - 1],
                                A[i - 1])
                      <= 0)
            k--;
        ans[k++] = A[i - 1];
    }
 
    // Resize the array to desired size
    ans.resize(k - 1);
 
    return ans;
}
 
// Driver code
int main()
{
    vector<Point> points;
 
    // Add points
    points.push_back({ 0, 3 });
    points.push_back({ 2, 2 });
    points.push_back({ 1, 1 });
    points.push_back({ 2, 1 });
    points.push_back({ 3, 0 });
    points.push_back({ 0, 0 });
    points.push_back({ 3, 3 });
 
    // Find the convex hull
    vector<Point> ans = convex_hull(points);
 
    // Print the convex hull
    for (int i = 0; i < ans.size(); i++)
        cout << "(" << ans[i].x << ", " << ans[i].y << ")"
             << endl;
 
    return 0;
}


Python3




class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
# A utility function to find next
# to top in a stack
 
 
def nextToTop(S):
    a = S.pop()
    b = S.pop()
    S.append(a)
    return b
 
# A utility function to swap two
# points
 
 
def swap(p1, p2):
    return p2, p1
 
# A utility function to return
# square of distance between
# two points
 
 
def distSq(p1, p2):
    return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)
 
# Prints convex hull of a set of n
# points.
 
 
def convexHull(points, n):
 
    # There must be at least 3 points
    if (n < 3):
        return
 
    # Initialize Result
    hull = []
 
    # Find the leftmost point
    l = 0
    for i in range(1, n):
        if (points[i].x < points[l].x):
            l = i
 
    # Start from leftmost point, keep
    # moving counterclockwise until
    # reach the start point again
    # This loop runs O(h) times where h is
    # number of points in result or output.
    p = l
    q = 0
    while (True):
 
        # Add current point to result
        hull.append(points[p])
 
        # Search for a point 'q' such that
        # orientation(p, x, q) is counterclockwise
        # for all points 'x'. The idea is to keep
        # track of last visited most counterclock-
        # wise point in q. If any point 'i' is more
        # counterclock-wise than q, then update q.
        q = (p + 1) % n
 
        for i in range(0, n):
 
            # If i is more counterclockwise than
            # current q, then update q
            if (orientation(points[p], points[i], points[q]) == 2):
                q = i
 
        # Now q is the most counterclockwise with
        # respect to p. Set p as q for next iteration,
        # so that q is added to result 'hull'
        p = q
 
        # While we don't come to first point
        if (p == l):
            break
 
    # Print Result
    printHull(hull)
 
# A utility function to return square
# of distance between p1 and p2
 
 
def distSq(p1, p2):
    return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)
 
# To find orientation of ordered triplet (p, q, r).
# The function returns following values
# 0 --> p, q and r are collinear
# 1 --> Clockwise
# 2 --> Counterclockwise
 
 
def orientation(p, q, r):
    val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
 
    if (val == 0):
        return 0  # collinear
    elif (val > 0):
        return 1   # clock or wise
    else:
        return 2   # counterclock or wise
 
# Prints convex hull of a set of n points.
 
 
def printHull(hull):
 
    print("The points in Convex Hull are:")
    for i in range(len(hull)):
        print("(", hull[i].x, ", ", hull[i].y, ")")
 
 
# Driver Code
if __name__ == "__main__":
 
    points = []
    points.append(Point(0, 3))
    points.append(Point(2, 2))
    points.append(Point(1, 1))
    points.append(Point(2, 1))
    points.append(Point(3, 0))
    points.append(Point(0, 0))
    points.append(Point(3, 3))
 
    n = len(points)
    convexHull(points, n)
 
    # This code is contributed by ishankhandelwals.


Javascript




// JS implementation of the approach
function Point(x, y) {
    this.x = x;
    this.y = y;
}
 
function crossProduct(O, A, B) {
    return (A.x - O.x) * (B.y - O.y) - (A.y - O.y) * (B.x - O.x);
}
 
function convexHull(A) {
    let n = A.length;
    let k = 0;
 
    if (n <= 3)
        return A;
 
    let ans = new Array(2 * n);
 
    // Sort points lexicographically
    A.sort((a, b) => {
        return a.x < b.x || (a.x == b.x && a.y < b.y);
    });
 
    // Build lower hull
    for (let i = 0; i < n; ++i) {
 
        // If the point at K-1 position is not a part
        // of hull as vector from ans[k-2] to ans[k-1]
        // and ans[k-2] to A[i] has a clockwise turn
        while (k >= 2 && crossProduct(ans[k - 2], ans[k - 1], A[i]) <= 0)
            k--;
        ans[k++] = A[i];
    }
 
    // Build upper hull
    for (let i = n - 1, t = k + 1; i > 0; --i) {
 
        // If the point at K-1 position is not a part
        // of hull as vector from ans[k-2] to ans[k-1]
        // and ans[k-2] to A[i] has a clockwise turn
        while (k >= t && crossProduct(ans[k - 2], ans[k - 1], A[i - 1]) <= 0)
            k--;
        ans[k++] = A[i - 1];
    }
 
    // Resize the array to desired size
    ans.length = k - 1;
 
    return ans;
}
 
// Driver code
let points = [];
 
// Add points
points.push(new Point(0, 3));
points.push(new Point(2, 2));
points.push(new Point(1, 1));
points.push(new Point(2, 1));
points.push(new Point(3, 0));
points.push(new Point(0, 0));
points.push(new Point(3, 3));
 
// Find the convex hull
let ans = convexHull(points);
 
// Print the convex hull
for (let i = 0; i < ans.length; i++)
    console.log("(" + ans[i].x + ", " + ans[i].y + ")");
     
    // This code is contributed by ishankhandelwals.


C#




using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
 
class Point {
    public int x;
    public int y;
 
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}
 
class HelloWorld {
 
    // A utility function to return square
    // of distance between p1 and p2
    public static int distSq(Point p1, Point p2)
    {
        return (p1.x - p2.x) * (p1.x - p2.x)
            + (p1.y - p2.y) * (p1.y - p2.y);
    }
 
    // To find orientation of ordered triplet (p, q, r).
    // The function returns following values
    // 0 --> p, q and r are collinear
    // 1 --> Clockwise
    // 2 --> Counterclockwise
    public static int orientation(Point p, Point q, Point r)
    {
        int val = (q.y - p.y) * (r.x - q.x)
                  - (q.x - p.x) * (r.y - q.y);
 
        if (val == 0)
            return 0;
        else if (val > 0)
            return 1;
        else
            return 2;
    }
 
    // Prints convex hull of a set of n points.
    public static void printHull(List<Point> hull)
    {
 
        Console.WriteLine("The points in Convex Hull are:");
        for (int i = 0; i < hull.Count; i++) {
            Console.WriteLine("(" + hull[i].x + ", "
                              + hull[i].y + ")");
        }
    }
 
    // Prints convex hull of a set of n
    // points.
    public static void convexHull(List<Point> points, int n)
    {
 
        // There must be at least 3 points
        if (n < 3) {
            return;
        }
 
        // Initialize Result
        List<Point> hull = new List<Point>();
 
        // Find the leftmost point
        int l = 0;
        for (int i = 1; i < n; i++) {
            if (points[i].x < points[l].x) {
                l = i;
            }
        }
 
        // Start from leftmost point, keep
        // moving counterclockwise until
        // reach the start point again
        // This loop runs O(h) times where h is
        // number of points in result or output.
        int p = l;
        int q = 0;
        while (true) {
 
            // Add current point to result
            hull.Add(points[p]);
 
            // Search for a point 'q' such that
            // orientation(p, x, q) is counterclockwise
            // for all points 'x'. The idea is to keep
            // track of last visited most counterclock-
            // wise point in q. If any point 'i' is more
            // counterclock-wise than q, then update q.
            q = (p + 1) % n;
 
            for (int i = 0; i < n; i++) {
 
                // If i is more counterclockwise than
                // current q, then update q
                if (orientation(points[p], points[i],
                                points[q])
                    == 2) {
                    q = i;
                }
            }
 
            // Now q is the most counterclockwise with
            // respect to p. Set p as q for next iteration,
            // so that q is added to result 'hull'
            p = q;
 
            // While we don't come to first point
            if (p == l) {
                break;
            }
        }
 
        // Print Result
        printHull(hull);
    }
 
    static void Main()
    {
        List<Point> points = new List<Point>();
 
        points.Add(new Point(0, 3));
        points.Add(new Point(2, 2));
        points.Add(new Point(1, 1));
        points.Add(new Point(2, 1));
        points.Add(new Point(3, 0));
        points.Add(new Point(0, 0));
        points.Add(new Point(3, 3));
 
        int n = points.Count;
        convexHull(points, n);
    }
}
 
// The code is contributed by Nidhi goel.


Java




import java.util.*;
 
class Point implements Comparable<Point> {
    long x, y;
 
    public Point(long x, long y)
    {
        this.x = x;
        this.y = y;
    }
    // Implement compareTo method for sorting
    @Override public int compareTo(Point p)
    {
        return Long.compare(x, p.x) != 0
            ? Long.compare(x, p.x)
            : Long.compare(y, p.y);
    }
}
 
public class ConvexHull {
    // Cross product of two vectors OA and OB
    // returns positive for counter clockwise
    // turn and negative for clockwise turn
    static long crossProduct(Point O, Point A, Point B)
    {
        return (A.x - O.x) * (B.y - O.y)
            - (A.y - O.y) * (B.x - O.x);
    }
    // Returns a list of points on the convex hull
    // in counter-clockwise order
    static List<Point> convexHull(List<Point> A)
    {
        int n = A.size(), k = 0;
 
        if (n <= 3)
            return A;
 
        List<Point> ans = new ArrayList<>(2 * n);
 
        // Sort points lexicographically
        Collections.sort(A);
 
        // Build lower hull
        for (int i = 0; i < n; ++i) {
            // If the point at K-1 position is not a part
            // of hull as vector from ans[k-2] to ans[k-1]
            // and ans[k-2] to A[i] has a clockwise turn
            while (k >= 2
                   && crossProduct(ans.get(k - 2),
                                   ans.get(k - 1), A.get(i))
                          <= 0)
                ans.remove(--k);
            ans.add(A.get(i));
            k++;
        }
 
        // Build upper hull
        for (int i = n - 2, t = k; i >= 0; --i) {
 
            // If the point at K-1 position is not a part
            // of hull as vector from ans[k-2] to ans[k-1]
            // and ans[k-2] to A[i] has a clockwise turn
            while (k > t
                   && crossProduct(ans.get(k - 2),
                                   ans.get(k - 1), A.get(i))
                          <= 0)
                ans.remove(--k);
            ans.add(A.get(i));
            k++;
        }
 
        // Resize the array to desired size
        ans.remove(ans.size() - 1);
 
        return ans;
    }
 
    // Driver code
    public static void main(String[] args)
    {
        List<Point> points = new ArrayList<>();
 
        // Add points
        points.add(new Point(0, 3));
        points.add(new Point(2, 2));
        points.add(new Point(1, 1));
        points.add(new Point(2, 1));
        points.add(new Point(3, 0));
        points.add(new Point(0, 0));
        points.add(new Point(3, 3));
 
        // Find the convex hull
        List<Point> ans = convexHull(points);
 
        // Print the convex hull
        for (Point p : ans)
            System.out.println("(" + p.x + ", " + p.y
                               + ")");
    }
}


Output: 

(0, 0)
(3, 0)
(3, 3)
(0, 3)

 



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads