Skip to content

Commit c860d8a

Browse files
committed
feat: Add ConvexHull new algorithm with Junit tests
1 parent 213fd5a commit c860d8a

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package com.thealgorithms.divideandconquer;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.HashSet;
6+
import java.util.List;
7+
import java.util.Objects;
8+
import java.util.Set;
9+
10+
class Point implements Comparable<Point> {
11+
double x, y;
12+
13+
public Point(double x, double y) {
14+
this.x = x;
15+
this.y = y;
16+
}
17+
18+
@Override
19+
public int compareTo(Point other) {
20+
if (this.x != other.x) {
21+
return Double.compare(this.x, other.x);
22+
}
23+
return Double.compare(this.y, other.y);
24+
}
25+
26+
@Override
27+
public boolean equals(Object obj) {
28+
if (!(obj instanceof Point)) return false;
29+
Point other = (Point) obj;
30+
return this.x == other.x && this.y == other.y;
31+
}
32+
33+
@Override
34+
public int hashCode() {
35+
return Objects.hash(x, y);
36+
}
37+
38+
@Override
39+
public String toString() {
40+
return String.format("(%.1f, %.1f)", x, y);
41+
}
42+
}
43+
44+
public class ConvexHull {
45+
private static double det(Point a, Point b, Point c) {
46+
return (a.x * b.y + b.x * c.y + c.x * a.y) - (a.y * b.x + b.y * c.x + c.y * a.x);
47+
}
48+
49+
public static List<Point> convexHullBruteForce(List<Point> points) {
50+
Collections.sort(points);
51+
Set<Point> convexSet = new HashSet<>();
52+
53+
for (int i = 0; i < points.size() - 1; i++) {
54+
for (int j = i + 1; j < points.size(); j++) {
55+
boolean pointsLeftOfIJ = false;
56+
boolean pointsRightOfIJ = false;
57+
boolean ijPartOfConvexHull = true;
58+
59+
for (int k = 0; k < points.size(); k++) {
60+
if (k != i && k != j) {
61+
double detK = det(points.get(i), points.get(j), points.get(k));
62+
63+
if (detK > 0) {
64+
pointsLeftOfIJ = true;
65+
} else if (detK < 0) {
66+
pointsRightOfIJ = true;
67+
} else if (points.get(k).compareTo(points.get(i)) < 0 || points.get(k).compareTo(points.get(j)) > 0) {
68+
ijPartOfConvexHull = false;
69+
break;
70+
}
71+
}
72+
73+
if (pointsLeftOfIJ && pointsRightOfIJ) {
74+
ijPartOfConvexHull = false;
75+
break;
76+
}
77+
}
78+
79+
if (ijPartOfConvexHull) {
80+
convexSet.add(points.get(i));
81+
convexSet.add(points.get(j));
82+
}
83+
}
84+
}
85+
86+
List<Point> result = new ArrayList<>(convexSet);
87+
Collections.sort(result);
88+
return result;
89+
}
90+
91+
public static List<Point> convexHullRecursive(List<Point> points) {
92+
Collections.sort(points);
93+
Set<Point> convexSet = new HashSet<>();
94+
Point leftMostPoint = points.get(0);
95+
Point rightMostPoint = points.get(points.size() - 1);
96+
97+
convexSet.add(leftMostPoint);
98+
convexSet.add(rightMostPoint);
99+
100+
List<Point> upperHull = new ArrayList<>();
101+
List<Point> lowerHull = new ArrayList<>();
102+
103+
for (int i = 1; i < points.size() - 1; i++) {
104+
double det = det(leftMostPoint, rightMostPoint, points.get(i));
105+
if (det > 0) {
106+
upperHull.add(points.get(i));
107+
} else if (det < 0) {
108+
lowerHull.add(points.get(i));
109+
}
110+
}
111+
112+
constructHull(upperHull, leftMostPoint, rightMostPoint, convexSet);
113+
constructHull(lowerHull, rightMostPoint, leftMostPoint, convexSet);
114+
115+
List<Point> result = new ArrayList<>(convexSet);
116+
Collections.sort(result);
117+
return result;
118+
}
119+
120+
private static void constructHull(List<Point> points, Point left, Point right, Set<Point> convexSet) {
121+
if (!points.isEmpty()) {
122+
Point extremePoint = null;
123+
double extremePointDistance = Double.NEGATIVE_INFINITY;
124+
List<Point> candidatePoints = new ArrayList<>();
125+
126+
for (Point p : points) {
127+
double det = det(left, right, p);
128+
if (det > 0) {
129+
candidatePoints.add(p);
130+
if (det > extremePointDistance) {
131+
extremePointDistance = det;
132+
extremePoint = p;
133+
}
134+
}
135+
}
136+
137+
if (extremePoint != null) {
138+
constructHull(candidatePoints, left, extremePoint, convexSet);
139+
convexSet.add(extremePoint);
140+
constructHull(candidatePoints, extremePoint, right, convexSet);
141+
}
142+
}
143+
}
144+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.thealgorithms.divideandconquer;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.util.Arrays;
6+
import java.util.List;
7+
import org.junit.jupiter.api.Test;
8+
9+
public class ConvexHullTest {
10+
11+
@Test
12+
void testConvexHullBruteForce() {
13+
List<Point> points = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 1));
14+
List<Point> expected = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 1));
15+
assertEquals(expected, ConvexHull.convexHullBruteForce(points));
16+
17+
points = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 0));
18+
expected = Arrays.asList(new Point(0, 0), new Point(10, 0));
19+
assertEquals(expected, ConvexHull.convexHullBruteForce(points));
20+
21+
points = Arrays.asList(new Point(-1, 1), new Point(-1, -1), new Point(0, 0), new Point(0.5, 0.5), new Point(1, -1), new Point(1, 1), new Point(-0.75, 1));
22+
expected = Arrays.asList(new Point(-1, -1), new Point(-1, 1), new Point(1, -1), new Point(1, 1));
23+
assertEquals(expected, ConvexHull.convexHullBruteForce(points));
24+
25+
points = Arrays.asList(new Point(0, 3), new Point(2, 2), new Point(1, 1), new Point(2, 1), new Point(3, 0), new Point(0, 0), new Point(3, 3), new Point(2, -1), new Point(2, -4), new Point(1, -3));
26+
expected = Arrays.asList(new Point(0, 0), new Point(0, 3), new Point(1, -3), new Point(2, -4), new Point(3, 0), new Point(3, 3));
27+
assertEquals(expected, ConvexHull.convexHullBruteForce(points));
28+
}
29+
30+
@Test
31+
void testConvexHullRecursive() {
32+
List<Point> points = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 1));
33+
List<Point> expected = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 1));
34+
assertEquals(expected, ConvexHull.convexHullRecursive(points));
35+
36+
points = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 0));
37+
expected = Arrays.asList(new Point(0, 0), new Point(10, 0));
38+
assertEquals(expected, ConvexHull.convexHullRecursive(points));
39+
40+
points = Arrays.asList(new Point(-1, 1), new Point(-1, -1), new Point(0, 0), new Point(0.5, 0.5), new Point(1, -1), new Point(1, 1), new Point(-0.75, 1));
41+
expected = Arrays.asList(new Point(-1, -1), new Point(-1, 1), new Point(1, -1), new Point(1, 1));
42+
assertEquals(expected, ConvexHull.convexHullRecursive(points));
43+
44+
points = Arrays.asList(new Point(0, 3), new Point(2, 2), new Point(1, 1), new Point(2, 1), new Point(3, 0), new Point(0, 0), new Point(3, 3), new Point(2, -1), new Point(2, -4), new Point(1, -3));
45+
expected = Arrays.asList(new Point(0, 0), new Point(0, 3), new Point(1, -3), new Point(2, -4), new Point(3, 0), new Point(3, 3));
46+
assertEquals(expected, ConvexHull.convexHullRecursive(points));
47+
}
48+
}

0 commit comments

Comments
 (0)