1
1
package com .thealgorithms .geometry ;
2
2
3
+ import java .util .ArrayList ;
3
4
import java .util .Arrays ;
4
5
import java .util .Comparator ;
5
6
import java .util .Stack ;
6
7
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 O(nlog(n)).
10
- * O(n) space complexity
8
+ /**
9
+ * A Java program that computes the convex hull using the Graham Scan algorithm.
10
+ * The time complexity is O(n) in the best case and O(n log(n)) in the worst case.
11
+ * The space complexity is O(n).
12
+ * This algorithm is applicable only to integral coordinates.
11
13
*
12
- * This algorithm is only applicable to integral coordinates.
13
- *
14
- * Reference:
14
+ * References:
15
15
* https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_algorithm.cpp
16
16
* https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_functions.hpp
17
17
* https://algs4.cs.princeton.edu/99hull/GrahamScan.java.html
18
18
*/
19
19
public class GrahamScan {
20
+
20
21
private final Stack <Point > hull = new Stack <>();
21
22
22
23
public GrahamScan (Point [] points ) {
23
-
24
- /*
25
- * pre-process the points by sorting them with respect to the bottom-most point, then we'll
26
- * push the first point in the array to be our first extreme point.
27
- */
24
+ // Pre-process points: sort by y-coordinate, then by polar order with respect to the first point
28
25
Arrays .sort (points );
29
26
Arrays .sort (points , 1 , points .length , points [0 ].polarOrder ());
27
+
30
28
hull .push (points [0 ]);
31
29
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 ])) {
30
+ // Find the first point not equal to points [0] (firstNonEqualIndex)
31
+ // and the first point not collinear firstNonCollinearIndex with the previous points
32
+ int firstNonEqualIndex ;
33
+ for (firstNonEqualIndex = 1 ; firstNonEqualIndex < points .length ; firstNonEqualIndex ++) {
34
+ if (!points [0 ].equals (points [firstNonEqualIndex ])) {
37
35
break ;
38
36
}
39
37
}
40
- if (indexPoint1 == points .length ) {
38
+
39
+ if (firstNonEqualIndex == points .length ) {
41
40
return ;
42
41
}
43
42
44
- int indexPoint2 ;
45
- for (indexPoint2 = indexPoint1 + 1 ; indexPoint2 < points .length ; indexPoint2 ++) {
46
- if (Point .orientation (points [0 ], points [indexPoint1 ], points [indexPoint2 ]) != 0 ) {
43
+ int firstNonCollinearIndex ;
44
+ for (firstNonCollinearIndex = firstNonEqualIndex + 1 ; firstNonCollinearIndex < points .length ; firstNonCollinearIndex ++) {
45
+ if (Point .orientation (points [0 ], points [firstNonEqualIndex ], points [firstNonCollinearIndex ]) != 0 ) {
47
46
break ;
48
47
}
49
48
}
50
- hull .push (points [indexPoint2 - 1 ]);
51
49
52
- // Now we simply add the point to the stack based on the orientation.
53
- for (int i = indexPoint2 ; i < points .length ; i ++) {
50
+ hull .push (points [firstNonCollinearIndex - 1 ]);
51
+
52
+ // Process the remaining points and update the hull
53
+ for (int i = firstNonCollinearIndex ; i < points .length ; i ++) {
54
54
Point top = hull .pop ();
55
55
while (Point .orientation (hull .peek (), top , points [i ]) <= 0 ) {
56
56
top = hull .pop ();
@@ -61,14 +61,10 @@ public GrahamScan(Point[] points) {
61
61
}
62
62
63
63
/**
64
- * @return A stack of points representing the convex hull.
64
+ * @return An iterable collection of points representing the convex hull.
65
65
*/
66
66
public Iterable <Point > hull () {
67
- Stack <Point > s = new Stack <>();
68
- for (Point p : hull ) {
69
- s .push (p );
70
- }
71
- return s ;
67
+ return new ArrayList <>(hull );
72
68
}
73
69
74
70
public record Point (int x , int y ) implements Comparable <Point > {
@@ -98,80 +94,65 @@ public int y() {
98
94
}
99
95
100
96
/**
101
- * Finds the orientation of ordered triplet.
97
+ * Determines the orientation of the triplet (a, b, c) .
102
98
*
103
- * @param a Co-ordinates of point a <int, int>
104
- * @param b Co-ordinates of point a <int, int>
105
- * @param c Co-ordinates of point a <int, int>
106
- * @return { -1, 0, +1 } if a -→ b -→ c is a { clockwise, collinear; counterclockwise }
107
- * turn.
99
+ * @param a The first point
100
+ * @param b The second point
101
+ * @param c The third point
102
+ * @return -1 if (a, b, c) is clockwise, 0 if collinear, +1 if counterclockwise
108
103
*/
109
104
public static int orientation (Point a , Point b , Point c ) {
110
105
int val = (b .x - a .x ) * (c .y - a .y ) - (b .y - a .y ) * (c .x - a .x );
111
- if (val == 0 ) {
112
- return 0 ;
113
- }
114
- return (val > 0 ) ? +1 : -1 ;
106
+ return Integer .compare (val , 0 );
115
107
}
116
108
117
109
/**
118
- * @param p2 Co-ordinate of point to compare to .
119
- * This function will compare the points and will return a positive integer if the
120
- * point is greater than the argument point and a negative integer if the point is
121
- * less than the argument point.
110
+ * Compares this point with another point .
111
+ *
112
+ * @param p2 The point to compare to
113
+ * @return A positive integer if this point is greater, a negative integer if less, or 0 if equal
122
114
*/
115
+ @ Override
123
116
public int compareTo (Point p2 ) {
124
- int res = Integer .compare (this .y , p2 .y );
125
- if (res == 0 ) {
126
- res = Integer .compare (this .x , p2 .x );
127
- }
128
- return res ;
117
+ int cmpY = Integer .compare (this .y , p2 .y );
118
+ return cmpY != 0 ? cmpY : Integer .compare (this .x , p2 .x );
129
119
}
130
120
131
121
/**
132
- * A helper function that will let us sort points by their polar order
133
- * This function will compare the angle between 2 polar Co-ordinates
122
+ * Returns a comparator to sort points by their polar order relative to this point.
134
123
*
135
- * @return the comparator
124
+ * @return A polar order comparator
136
125
*/
137
126
public Comparator <Point > polarOrder () {
138
127
return new PolarOrder ();
139
128
}
140
129
141
130
private final class PolarOrder implements Comparator <Point > {
131
+ @ Override
142
132
public int compare (Point p1 , Point p2 ) {
143
133
int dx1 = p1 .x - x ;
144
134
int dy1 = p1 .y - y ;
145
135
int dx2 = p2 .x - x ;
146
136
int dy2 = p2 .y - y ;
147
137
148
138
if (dy1 >= 0 && dy2 < 0 ) {
149
- return -1 ; // q1 above; q2 below
139
+ return -1 ; // p1 above p2
150
140
} else if (dy2 >= 0 && dy1 < 0 ) {
151
- return +1 ; // q1 below; q2 above
152
- } else if (dy1 == 0 && dy2 == 0 ) { // 3-collinear and horizontal
153
- if (dx1 >= 0 && dx2 < 0 ) {
154
- return -1 ;
155
- } else if (dx2 >= 0 && dx1 < 0 ) {
156
- return +1 ;
157
- } else {
158
- return 0 ;
159
- }
141
+ return 1 ; // p1 below p2
142
+ } else if (dy1 == 0 && dy2 == 0 ) { // Collinear and horizontal
143
+ return Integer .compare (dx2 , dx1 );
160
144
} else {
161
- return -orientation (Point .this , p1 , p2 ); // both above or below
145
+ return -orientation (Point .this , p1 , p2 ); // Compare orientation
162
146
}
163
147
}
164
148
}
165
149
166
150
/**
167
- * Override of the toString method, necessary to compute the difference
168
- * between the expected result and the derived result
169
- *
170
- * @return a string representation of any given 2D point in the format (x, y)
151
+ * @return A string representation of this point in the format (x, y)
171
152
*/
172
153
@ Override
173
154
public String toString () {
174
- return "(" + x + ", " + y + ")" ;
155
+ return String . format ( "(%d, %d)" , x , y ) ;
175
156
}
176
157
}
177
158
}
0 commit comments