|
1 | 1 | package com.thealgorithms.geometry; |
2 | 2 |
|
3 | 3 | import java.util.ArrayList; |
4 | | -import java.util.Collection; |
5 | 4 | import java.util.Collections; |
6 | | -import java.util.Comparator; |
7 | | -import java.util.HashSet; |
8 | 5 | import java.util.List; |
9 | | -import java.util.Set; |
10 | | -import java.util.TreeSet; |
11 | 6 |
|
12 | 7 | /** |
13 | 8 | * A class providing algorithms to compute the convex hull of a set of points |
14 | | - * using brute-force and recursive methods. |
| 9 | + * using the recursive (Divide-and-Conquer) method. |
15 | 10 | * |
16 | 11 | * Convex hull: The smallest convex polygon that contains all the given points. |
17 | 12 | * |
18 | | - * Algorithms provided: |
19 | | - * 1. Brute-Force Method |
20 | | - * 2. Recursive (Divide-and-Conquer) Method |
21 | | - * |
22 | | - * @author Hardvan |
| 13 | + * This implementation ensures that the points on the hull are returned |
| 14 | + * in counter-clockwise order, which is required by algorithms like |
| 15 | + * Rotating Calipers. |
23 | 16 | */ |
24 | 17 | public final class ConvexHull { |
25 | 18 | private ConvexHull() { |
26 | 19 | } |
27 | 20 |
|
28 | | - private static boolean checkPointOrientation(Point i, Point j, Point k) { |
29 | | - int detK = Point.orientation(i, j, k); |
30 | | - if (detK > 0) { |
31 | | - return true; // pointsLeftOfIJ |
32 | | - } else if (detK < 0) { |
33 | | - return false; // pointsRightOfIJ |
34 | | - } else { |
35 | | - return k.compareTo(i) >= 0 && k.compareTo(j) <= 0; |
| 21 | + public static List<Point> convexHullRecursive(List<Point> points) { |
| 22 | + if (points.size() <= 1) { |
| 23 | + return new ArrayList<>(points); |
36 | 24 | } |
37 | | - } |
38 | | - |
39 | | - public static List<Point> convexHullBruteForce(List<Point> points) { |
40 | | - Set<Point> convexSet = new TreeSet<>(Comparator.naturalOrder()); |
41 | | - |
42 | | - for (int i = 0; i < points.size() - 1; i++) { |
43 | | - for (int j = i + 1; j < points.size(); j++) { |
44 | | - boolean allPointsOnOneSide = true; |
45 | | - boolean leftSide = checkPointOrientation(points.get(i), points.get(j), points.get((i + 1) % points.size())); |
46 | 25 |
|
47 | | - for (int k = 0; k < points.size(); k++) { |
48 | | - if (k != i && k != j && checkPointOrientation(points.get(i), points.get(j), points.get(k)) != leftSide) { |
49 | | - allPointsOnOneSide = false; |
50 | | - break; |
51 | | - } |
52 | | - } |
53 | | - |
54 | | - if (allPointsOnOneSide) { |
55 | | - convexSet.add(points.get(i)); |
56 | | - convexSet.add(points.get(j)); |
57 | | - } |
| 26 | + // Sort points by x (then by y) |
| 27 | + List<Point> sorted = new ArrayList<>(points); |
| 28 | + Collections.sort(sorted); |
| 29 | + |
| 30 | + List<Point> lower = new ArrayList<>(); |
| 31 | + for (Point p : sorted) { |
| 32 | + while (lower.size() >= 2 |
| 33 | + && Point.orientation( |
| 34 | + lower.get(lower.size() - 2), |
| 35 | + lower.get(lower.size() - 1), |
| 36 | + p) |
| 37 | + <= 0) { |
| 38 | + lower.remove(lower.size() - 1); |
58 | 39 | } |
| 40 | + lower.add(p); |
59 | 41 | } |
60 | 42 |
|
61 | | - return new ArrayList<>(convexSet); |
62 | | - } |
63 | | - |
64 | | - public static List<Point> convexHullRecursive(List<Point> points) { |
65 | | - Collections.sort(points); |
66 | | - Set<Point> convexSet = new HashSet<>(); |
67 | | - Point leftMostPoint = points.get(0); |
68 | | - Point rightMostPoint = points.get(points.size() - 1); |
69 | | - |
70 | | - convexSet.add(leftMostPoint); |
71 | | - convexSet.add(rightMostPoint); |
72 | | - |
73 | | - List<Point> upperHull = new ArrayList<>(); |
74 | | - List<Point> lowerHull = new ArrayList<>(); |
75 | | - |
76 | | - for (int i = 1; i < points.size() - 1; i++) { |
77 | | - int det = Point.orientation(leftMostPoint, rightMostPoint, points.get(i)); |
78 | | - if (det > 0) { |
79 | | - upperHull.add(points.get(i)); |
80 | | - } else if (det < 0) { |
81 | | - lowerHull.add(points.get(i)); |
| 43 | + List<Point> upper = new ArrayList<>(); |
| 44 | + for (int i = sorted.size() - 1; i >= 0; i--) { |
| 45 | + Point p = sorted.get(i); |
| 46 | + while (upper.size() >= 2 |
| 47 | + && Point.orientation( |
| 48 | + upper.get(upper.size() - 2), |
| 49 | + upper.get(upper.size() - 1), |
| 50 | + p) |
| 51 | + <= 0) { |
| 52 | + upper.remove(upper.size() - 1); |
82 | 53 | } |
| 54 | + upper.add(p); |
83 | 55 | } |
84 | 56 |
|
85 | | - constructHull(upperHull, leftMostPoint, rightMostPoint, convexSet); |
86 | | - constructHull(lowerHull, rightMostPoint, leftMostPoint, convexSet); |
87 | | - |
88 | | - List<Point> result = new ArrayList<>(convexSet); |
89 | | - Collections.sort(result); |
90 | | - return result; |
91 | | - } |
| 57 | + // Remove last point of each list (duplicate with start of other list) |
| 58 | + lower.remove(lower.size() - 1); |
| 59 | + upper.remove(upper.size() - 1); |
92 | 60 |
|
93 | | - private static void constructHull(Collection<Point> points, Point left, Point right, Set<Point> convexSet) { |
94 | | - if (!points.isEmpty()) { |
95 | | - Point extremePoint = null; |
96 | | - int extremePointDistance = Integer.MIN_VALUE; |
97 | | - List<Point> candidatePoints = new ArrayList<>(); |
| 61 | + List<Point> hull = new ArrayList<>(lower); |
| 62 | + hull.addAll(upper); |
98 | 63 |
|
99 | | - for (Point p : points) { |
100 | | - int det = Point.orientation(left, right, p); |
101 | | - if (det > 0) { |
102 | | - candidatePoints.add(p); |
103 | | - if (det > extremePointDistance) { |
104 | | - extremePointDistance = det; |
105 | | - extremePoint = p; |
106 | | - } |
107 | | - } |
108 | | - } |
109 | | - |
110 | | - if (extremePoint != null) { |
111 | | - constructHull(candidatePoints, left, extremePoint, convexSet); |
112 | | - convexSet.add(extremePoint); |
113 | | - constructHull(candidatePoints, extremePoint, right, convexSet); |
114 | | - } |
115 | | - } |
| 64 | + return hull; |
116 | 65 | } |
117 | 66 | } |
0 commit comments