Skip to content

Commit 6d13d95

Browse files
Graham scan algorithm (TheAlgorithms#3903)
* Added Graham scan algorithm TheAlgorithms#3894 * Added Graham scan algorithm (TheAlgorithms#3894) --------- Co-authored-by: Stronshade <diegobrocker1999@gmail.com>
1 parent be13981 commit 6d13d95

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package com.thealgorithms.geometry;
2+
3+
import java.util.Arrays;
4+
import java.util.Comparator;
5+
import java.util.Stack;
6+
7+
/*
8+
* A Java program that computes the convex hull using the Graham Scan algorithm
9+
* In the best case, time complexity is O(n), while in the worst case, it is log(n).
10+
* O(n) space complexity
11+
*
12+
* This algorithm is only applicable to integral coordinates.
13+
*
14+
* Reference:
15+
* https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_algorithm.cpp
16+
* https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_functions.hpp
17+
* https://algs4.cs.princeton.edu/99hull/GrahamScan.java.html
18+
*/
19+
public class GrahamScan {
20+
private final Stack<Point> hull = new Stack<>();
21+
22+
public GrahamScan(Point[] points) {
23+
24+
/*
25+
* pre-process the points by sorting them with respect to the bottom-most point, then we'll push the
26+
* first point in the array to be our first extreme point.
27+
*/
28+
Arrays.sort(points);
29+
Arrays.sort(points, 1, points.length, points[0].polarOrder());
30+
hull.push(points[0]);
31+
32+
// find index of first point not equal to a[0] (indexPoint1) and the first point that's not
33+
// collinear with either (indexPoint2).
34+
int indexPoint1;
35+
for (indexPoint1 = 1; indexPoint1 < points.length; indexPoint1++)
36+
if (!points[0].equals(points[indexPoint1])) break;
37+
if (indexPoint1 == points.length) return;
38+
39+
int indexPoint2;
40+
for (indexPoint2 = indexPoint1+1; indexPoint2 < points.length; indexPoint2++)
41+
if (Point.orientation(points[0], points[indexPoint1], points[indexPoint2]) != 0) break;
42+
hull.push(points[indexPoint2-1]);
43+
44+
// Now we simply add the point to the stack based on the orientation.
45+
for (int i = indexPoint2; i < points.length; i++) {
46+
Point top = hull.pop();
47+
while (Point.orientation(hull.peek(), top, points[i]) <= 0) {
48+
top = hull.pop();
49+
}
50+
hull.push(top);
51+
hull.push(points[i]);
52+
}
53+
}
54+
55+
/**
56+
* @return A stack of points representing the convex hull.
57+
*/
58+
public Iterable<Point> hull() {
59+
Stack<Point> s = new Stack<>();
60+
for (Point p : hull) s.push(p);
61+
return s;
62+
}
63+
64+
public record Point(int x, int y) implements Comparable<Point> {
65+
66+
/**
67+
* Default constructor
68+
* @param x x-coordinate
69+
* @param y y-coordinate
70+
*/
71+
public Point { }
72+
73+
/**
74+
* @return the x-coordinate
75+
*/
76+
@Override
77+
public int x() {
78+
return x;
79+
}
80+
81+
/**
82+
* @return the y-coordinate
83+
*/
84+
@Override
85+
public int y() { return y; }
86+
87+
/**
88+
* Finds the orientation of ordered triplet.
89+
*
90+
* @param a Co-ordinates of point a <int, int>
91+
* @param b Co-ordinates of point a <int, int>
92+
* @param c Co-ordinates of point a <int, int>
93+
* @return { -1, 0, +1 } if a -→ b -→ c is a { clockwise, collinear; counterclockwise } turn.
94+
*/
95+
public static int orientation(Point a, Point b, Point c) {
96+
int val = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
97+
if (val == 0) {
98+
return 0;
99+
}
100+
return (val > 0) ? +1 : -1;
101+
}
102+
103+
/**
104+
* @param p2 Co-ordinate of point to compare to.
105+
* This function will compare the points and will return a positive integer it the
106+
* point is greater than the argument point and a negative integer if the point is
107+
* less than the argument point.
108+
*/
109+
public int compareTo(Point p2) {
110+
if (this.y < p2.y) return -1;
111+
if (this.y > p2.y) return +1;
112+
if (this.x < p2.x) return -1;
113+
if (this.x > p2.x) return +1;
114+
return 0;
115+
}
116+
117+
/**
118+
* A helper function that will let us sort points by their polar order
119+
* This function will compare the angle between 2 polar Co-ordinates
120+
*
121+
* @return the comparator
122+
*/
123+
public Comparator<Point> polarOrder() {
124+
return new PolarOrder();
125+
}
126+
127+
private class PolarOrder implements Comparator<Point> {
128+
public int compare(Point p1, Point p2) {
129+
int dx1 = p1.x - x;
130+
int dy1 = p1.y - y;
131+
int dx2 = p2.x - x;
132+
int dy2 = p2.y - y;
133+
134+
if (dy1 >= 0 && dy2 < 0) return -1; // q1 above; q2 below
135+
else if (dy2 >= 0 && dy1 < 0) return +1; // q1 below; q2 above
136+
else if (dy1 == 0 && dy2 == 0) { // 3-collinear and horizontal
137+
if (dx1 >= 0 && dx2 < 0) return -1;
138+
else if (dx2 >= 0 && dx1 < 0) return +1;
139+
else return 0;
140+
} else return -orientation(Point.this, p1, p2); // both above or below
141+
}
142+
}
143+
144+
/**
145+
* Override of the toString method, necessary to compute the difference
146+
* between the expected result and the derived result
147+
*
148+
* @return a string representation of any given 2D point in the format (x, y)
149+
*/
150+
@Override
151+
public String toString() {
152+
return "(" + x + ", " + y + ")";
153+
}
154+
}
155+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.thealgorithms.geometry;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
7+
public class GrahamScanTest {
8+
@Test
9+
void testGrahamScan() {
10+
GrahamScan.Point[] points = {new GrahamScan.Point(0, 3), new GrahamScan.Point(1, 1),
11+
new GrahamScan.Point(2, 2), new GrahamScan.Point(4, 4),
12+
new GrahamScan.Point(0, 0), new GrahamScan.Point(1, 2),
13+
new GrahamScan.Point(3, 1), new GrahamScan.Point(3, 3)};
14+
String expectedResult = "[(0, 0), (3, 1), (4, 4), (0, 3)]";
15+
16+
GrahamScan graham = new GrahamScan(points);
17+
assertEquals(expectedResult, graham.hull().toString());
18+
}
19+
}

0 commit comments

Comments
 (0)