package com.thealgorithms.geometry; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; /** * A class providing algorithms to compute the convex hull of a set of points * using brute-force and recursive methods. * * Convex hull: The smallest convex polygon that contains all the given points. * * Algorithms provided: * 1. Brute-Force Method * 2. Recursive (Divide-and-Conquer) Method * * @author Hardvan */ public final class ConvexHull { private ConvexHull() { } private static boolean checkPointOrientation(Point i, Point j, Point k) { int detK = Point.orientation(i, j, k); if (detK > 0) { return true; // pointsLeftOfIJ } else if (detK < 0) { return false; // pointsRightOfIJ } else { return k.compareTo(i) >= 0 && k.compareTo(j) <= 0; } } public static List convexHullBruteForce(List points) { Set convexSet = new TreeSet<>(Comparator.naturalOrder()); for (int i = 0; i < points.size() - 1; i++) { for (int j = i + 1; j < points.size(); j++) { boolean allPointsOnOneSide = true; boolean leftSide = checkPointOrientation(points.get(i), points.get(j), points.get((i + 1) % points.size())); for (int k = 0; k < points.size(); k++) { if (k != i && k != j && checkPointOrientation(points.get(i), points.get(j), points.get(k)) != leftSide) { allPointsOnOneSide = false; break; } } if (allPointsOnOneSide) { convexSet.add(points.get(i)); convexSet.add(points.get(j)); } } } return new ArrayList<>(convexSet); } public static List convexHullRecursive(List points) { Collections.sort(points); Set convexSet = new HashSet<>(); Point leftMostPoint = points.get(0); Point rightMostPoint = points.get(points.size() - 1); convexSet.add(leftMostPoint); convexSet.add(rightMostPoint); List upperHull = new ArrayList<>(); List lowerHull = new ArrayList<>(); for (int i = 1; i < points.size() - 1; i++) { int det = Point.orientation(leftMostPoint, rightMostPoint, points.get(i)); if (det > 0) { upperHull.add(points.get(i)); } else if (det < 0) { lowerHull.add(points.get(i)); } } constructHull(upperHull, leftMostPoint, rightMostPoint, convexSet); constructHull(lowerHull, rightMostPoint, leftMostPoint, convexSet); List result = new ArrayList<>(convexSet); Collections.sort(result); return result; } private static void constructHull(Collection points, Point left, Point right, Set convexSet) { if (!points.isEmpty()) { Point extremePoint = null; int extremePointDistance = Integer.MIN_VALUE; List candidatePoints = new ArrayList<>(); for (Point p : points) { int det = Point.orientation(left, right, p); if (det > 0) { candidatePoints.add(p); if (det > extremePointDistance) { extremePointDistance = det; extremePoint = p; } } } if (extremePoint != null) { constructHull(candidatePoints, left, extremePoint, convexSet); convexSet.add(extremePoint); constructHull(candidatePoints, extremePoint, right, convexSet); } } } }