Skip to content

Update GrahamScan.java #5310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Aug 9, 2024
93 changes: 36 additions & 57 deletions src/main/java/com/thealgorithms/geometry/GrahamScan.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
import java.util.Comparator;
import java.util.Stack;

/*
* A Java program that computes the convex hull using the Graham Scan algorithm
* In the best case, time complexity is O(n), while in the worst case, it is O(nlog(n)).
* O(n) space complexity
/**
* A Java program that computes the convex hull using the Graham Scan algorithm.
* The time complexity is O(n) in the best case and O(n log(n)) in the worst case.
* The space complexity is O(n).
* This algorithm is applicable only to integral coordinates.
*
* This algorithm is only applicable to integral coordinates.
*
* Reference:
* References:
* https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_algorithm.cpp
* https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_functions.hpp
* https://algs4.cs.princeton.edu/99hull/GrahamScan.java.html
Expand All @@ -20,17 +19,12 @@ public class GrahamScan {
private final Stack<Point> hull = new Stack<>();

public GrahamScan(Point[] points) {

/*
* pre-process the points by sorting them with respect to the bottom-most point, then we'll
* push the first point in the array to be our first extreme point.
*/
// Pre-process points: sort by y-coordinate, then by polar order with respect to the first point
Arrays.sort(points);
Arrays.sort(points, 1, points.length, points[0].polarOrder());
hull.push(points[0]);

// find index of first point not equal to a[0] (indexPoint1) and the first point that's not
// collinear with either (indexPoint2).
// Find the first point not equal to points[0] and the first point not collinear with the previous points
int indexPoint1;
for (indexPoint1 = 1; indexPoint1 < points.length; indexPoint1++) {
if (!points[0].equals(points[indexPoint1])) {
Expand All @@ -49,7 +43,7 @@ public GrahamScan(Point[] points) {
}
hull.push(points[indexPoint2 - 1]);

// Now we simply add the point to the stack based on the orientation.
// Process the remaining points and update the hull
for (int i = indexPoint2; i < points.length; i++) {
Point top = hull.pop();
while (Point.orientation(hull.peek(), top, points[i]) <= 0) {
Expand All @@ -61,14 +55,14 @@ public GrahamScan(Point[] points) {
}

/**
* @return A stack of points representing the convex hull.
* @return An iterable collection of points representing the convex hull.
*/
public Iterable<Point> hull() {
Stack<Point> s = new Stack<>();
Stack<Point> result = new Stack<>();
for (Point p : hull) {
s.push(p);
result.push(p);
}
return s;
return result;
}

public record Point(int x, int y) implements Comparable<Point> {
Expand Down Expand Up @@ -98,80 +92,65 @@ public int y() {
}

/**
* Finds the orientation of ordered triplet.
* Determines the orientation of the triplet (a, b, c).
*
* @param a Co-ordinates of point a <int, int>
* @param b Co-ordinates of point a <int, int>
* @param c Co-ordinates of point a <int, int>
* @return { -1, 0, +1 } if a -→ b -→ c is a { clockwise, collinear; counterclockwise }
* turn.
* @param a The first point
* @param b The second point
* @param c The third point
* @return -1 if (a, b, c) is clockwise, 0 if collinear, +1 if counterclockwise
*/
public static int orientation(Point a, Point b, Point c) {
int val = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
if (val == 0) {
return 0;
}
return (val > 0) ? +1 : -1;
return Integer.compare(val, 0);
}

/**
* @param p2 Co-ordinate of point to compare to.
* This function will compare the points and will return a positive integer if the
* point is greater than the argument point and a negative integer if the point is
* less than the argument point.
* Compares this point with another point.
*
* @param p2 The point to compare to
* @return A positive integer if this point is greater, a negative integer if less, or 0 if equal
*/
@Override
public int compareTo(Point p2) {
int res = Integer.compare(this.y, p2.y);
if (res == 0) {
res = Integer.compare(this.x, p2.x);
}
return res;
int cmpY = Integer.compare(this.y, p2.y);
return cmpY != 0 ? cmpY : Integer.compare(this.x, p2.x);
}

/**
* A helper function that will let us sort points by their polar order
* This function will compare the angle between 2 polar Co-ordinates
* Returns a comparator to sort points by their polar order relative to this point.
*
* @return the comparator
* @return A polar order comparator
*/
public Comparator<Point> polarOrder() {
return new PolarOrder();
}

private final class PolarOrder implements Comparator<Point> {
@Override
public int compare(Point p1, Point p2) {
int dx1 = p1.x - x;
int dy1 = p1.y - y;
int dx2 = p2.x - x;
int dy2 = p2.y - y;

if (dy1 >= 0 && dy2 < 0) {
return -1; // q1 above; q2 below
return -1; // p1 above p2
} else if (dy2 >= 0 && dy1 < 0) {
return +1; // q1 below; q2 above
} else if (dy1 == 0 && dy2 == 0) { // 3-collinear and horizontal
if (dx1 >= 0 && dx2 < 0) {
return -1;
} else if (dx2 >= 0 && dx1 < 0) {
return +1;
} else {
return 0;
}
return 1; // p1 below p2
} else if (dy1 == 0 && dy2 == 0) { // Collinear and horizontal
return Integer.compare(dx2, dx1);
} else {
return -orientation(Point.this, p1, p2); // both above or below
return -orientation(Point.this, p1, p2); // Compare orientation
}
}
}

/**
* Override of the toString method, necessary to compute the difference
* between the expected result and the derived result
*
* @return a string representation of any given 2D point in the format (x, y)
* @return A string representation of this point in the format (x, y)
*/
@Override
public String toString() {
return "(" + x + ", " + y + ")";
return String.format("(%d, %d)", x, y);
}
}
}