From 324a35a939e86eda64af74aad37c2d8536caed7f Mon Sep 17 00:00:00 2001 From: Bayram Turgut <137455737+bayramtturgutt@users.noreply.github.com> Date: Fri, 9 Aug 2024 15:03:54 +0300 Subject: [PATCH] Update GrahamScan.java (#5310) * Update GrahamScan.java improved the Javadoc comments, clarified some methods in the Point class, and corrected some text. * Minor adjustment to GrahamScan.java * revised GrahamScan.java * Update-2 GrahamScan.java * clang format GrahamScan.java * reverted GrahamScan.java * minor updates.java * minor updates * Spc.java * clang format --------- Co-authored-by: Alex Klymenko --- .../thealgorithms/geometry/GrahamScan.java | 127 ++++++++---------- 1 file changed, 54 insertions(+), 73 deletions(-) diff --git a/src/main/java/com/thealgorithms/geometry/GrahamScan.java b/src/main/java/com/thealgorithms/geometry/GrahamScan.java index 2773d03b..1a361378 100644 --- a/src/main/java/com/thealgorithms/geometry/GrahamScan.java +++ b/src/main/java/com/thealgorithms/geometry/GrahamScan.java @@ -1,56 +1,56 @@ package com.thealgorithms.geometry; +import java.util.ArrayList; import java.util.Arrays; 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 */ public class GrahamScan { + private final Stack 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). - int indexPoint1; - for (indexPoint1 = 1; indexPoint1 < points.length; indexPoint1++) { - if (!points[0].equals(points[indexPoint1])) { + // Find the first point not equal to points[0] (firstNonEqualIndex) + // and the first point not collinear firstNonCollinearIndex with the previous points + int firstNonEqualIndex; + for (firstNonEqualIndex = 1; firstNonEqualIndex < points.length; firstNonEqualIndex++) { + if (!points[0].equals(points[firstNonEqualIndex])) { break; } } - if (indexPoint1 == points.length) { + + if (firstNonEqualIndex == points.length) { return; } - int indexPoint2; - for (indexPoint2 = indexPoint1 + 1; indexPoint2 < points.length; indexPoint2++) { - if (Point.orientation(points[0], points[indexPoint1], points[indexPoint2]) != 0) { + int firstNonCollinearIndex; + for (firstNonCollinearIndex = firstNonEqualIndex + 1; firstNonCollinearIndex < points.length; firstNonCollinearIndex++) { + if (Point.orientation(points[0], points[firstNonEqualIndex], points[firstNonCollinearIndex]) != 0) { break; } } - hull.push(points[indexPoint2 - 1]); - // Now we simply add the point to the stack based on the orientation. - for (int i = indexPoint2; i < points.length; i++) { + hull.push(points[firstNonCollinearIndex - 1]); + + // Process the remaining points and update the hull + for (int i = firstNonCollinearIndex; i < points.length; i++) { Point top = hull.pop(); while (Point.orientation(hull.peek(), top, points[i]) <= 0) { top = hull.pop(); @@ -61,14 +61,10 @@ public class GrahamScan { } /** - * @return A stack of points representing the convex hull. + * @return An iterable collection of points representing the convex hull. */ public Iterable hull() { - Stack s = new Stack<>(); - for (Point p : hull) { - s.push(p); - } - return s; + return new ArrayList<>(hull); } public record Point(int x, int y) implements Comparable { @@ -98,47 +94,41 @@ public class GrahamScan { } /** - * Finds the orientation of ordered triplet. + * Determines the orientation of the triplet (a, b, c). * - * @param a Co-ordinates of point a - * @param b Co-ordinates of point a - * @param c Co-ordinates of point a - * @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. - */ - 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; - } - - /** - * A helper function that will let us sort points by their polar order - * This function will compare the angle between 2 polar Co-ordinates + * Compares this point with another point. * - * @return the comparator + * @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 cmpY = Integer.compare(this.y, p2.y); + return cmpY != 0 ? cmpY : Integer.compare(this.x, p2.x); + } + + /** + * Returns a comparator to sort points by their polar order relative to this point. + * + * @return A polar order comparator */ public Comparator polarOrder() { return new PolarOrder(); } private final class PolarOrder implements Comparator { + @Override public int compare(Point p1, Point p2) { int dx1 = p1.x - x; int dy1 = p1.y - y; @@ -146,32 +136,23 @@ public class GrahamScan { 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); } } }