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 <alexanderklmn@gmail.com>
This commit is contained in:
parent
5fc26239eb
commit
324a35a939
@ -1,56 +1,56 @@
|
|||||||
package com.thealgorithms.geometry;
|
package com.thealgorithms.geometry;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* A Java program that computes the convex hull using the Graham Scan algorithm
|
* 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)).
|
* The time complexity is O(n) in the best case and O(n log(n)) in the worst case.
|
||||||
* O(n) space complexity
|
* The space complexity is O(n).
|
||||||
|
* This algorithm is applicable only to integral coordinates.
|
||||||
*
|
*
|
||||||
* This algorithm is only applicable to integral coordinates.
|
* References:
|
||||||
*
|
|
||||||
* Reference:
|
|
||||||
* 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_algorithm.cpp
|
||||||
* https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_functions.hpp
|
* https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_functions.hpp
|
||||||
* https://algs4.cs.princeton.edu/99hull/GrahamScan.java.html
|
* https://algs4.cs.princeton.edu/99hull/GrahamScan.java.html
|
||||||
*/
|
*/
|
||||||
public class GrahamScan {
|
public class GrahamScan {
|
||||||
|
|
||||||
private final Stack<Point> hull = new Stack<>();
|
private final Stack<Point> hull = new Stack<>();
|
||||||
|
|
||||||
public GrahamScan(Point[] points) {
|
public GrahamScan(Point[] points) {
|
||||||
|
// Pre-process points: sort by y-coordinate, then by polar order with respect to the first point
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
Arrays.sort(points);
|
Arrays.sort(points);
|
||||||
Arrays.sort(points, 1, points.length, points[0].polarOrder());
|
Arrays.sort(points, 1, points.length, points[0].polarOrder());
|
||||||
|
|
||||||
hull.push(points[0]);
|
hull.push(points[0]);
|
||||||
|
|
||||||
// find index of first point not equal to a[0] (indexPoint1) and the first point that's not
|
// Find the first point not equal to points[0] (firstNonEqualIndex)
|
||||||
// collinear with either (indexPoint2).
|
// and the first point not collinear firstNonCollinearIndex with the previous points
|
||||||
int indexPoint1;
|
int firstNonEqualIndex;
|
||||||
for (indexPoint1 = 1; indexPoint1 < points.length; indexPoint1++) {
|
for (firstNonEqualIndex = 1; firstNonEqualIndex < points.length; firstNonEqualIndex++) {
|
||||||
if (!points[0].equals(points[indexPoint1])) {
|
if (!points[0].equals(points[firstNonEqualIndex])) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (indexPoint1 == points.length) {
|
|
||||||
|
if (firstNonEqualIndex == points.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int indexPoint2;
|
int firstNonCollinearIndex;
|
||||||
for (indexPoint2 = indexPoint1 + 1; indexPoint2 < points.length; indexPoint2++) {
|
for (firstNonCollinearIndex = firstNonEqualIndex + 1; firstNonCollinearIndex < points.length; firstNonCollinearIndex++) {
|
||||||
if (Point.orientation(points[0], points[indexPoint1], points[indexPoint2]) != 0) {
|
if (Point.orientation(points[0], points[firstNonEqualIndex], points[firstNonCollinearIndex]) != 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hull.push(points[indexPoint2 - 1]);
|
|
||||||
|
|
||||||
// Now we simply add the point to the stack based on the orientation.
|
hull.push(points[firstNonCollinearIndex - 1]);
|
||||||
for (int i = indexPoint2; i < points.length; i++) {
|
|
||||||
|
// Process the remaining points and update the hull
|
||||||
|
for (int i = firstNonCollinearIndex; i < points.length; i++) {
|
||||||
Point top = hull.pop();
|
Point top = hull.pop();
|
||||||
while (Point.orientation(hull.peek(), top, points[i]) <= 0) {
|
while (Point.orientation(hull.peek(), top, points[i]) <= 0) {
|
||||||
top = hull.pop();
|
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<Point> hull() {
|
public Iterable<Point> hull() {
|
||||||
Stack<Point> s = new Stack<>();
|
return new ArrayList<>(hull);
|
||||||
for (Point p : hull) {
|
|
||||||
s.push(p);
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Point(int x, int y) implements Comparable<Point> {
|
public record Point(int x, int y) implements Comparable<Point> {
|
||||||
@ -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 <int, int>
|
* @param a The first point
|
||||||
* @param b Co-ordinates of point a <int, int>
|
* @param b The second point
|
||||||
* @param c Co-ordinates of point a <int, int>
|
* @param c The third point
|
||||||
* @return { -1, 0, +1 } if a -→ b -→ c is a { clockwise, collinear; counterclockwise }
|
* @return -1 if (a, b, c) is clockwise, 0 if collinear, +1 if counterclockwise
|
||||||
* turn.
|
|
||||||
*/
|
*/
|
||||||
public static int orientation(Point a, Point b, Point c) {
|
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);
|
int val = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
||||||
if (val == 0) {
|
return Integer.compare(val, 0);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return (val > 0) ? +1 : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param p2 Co-ordinate of point to compare to.
|
* Compares this point with another point.
|
||||||
* 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
|
|
||||||
*
|
*
|
||||||
* @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<Point> polarOrder() {
|
public Comparator<Point> polarOrder() {
|
||||||
return new PolarOrder();
|
return new PolarOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class PolarOrder implements Comparator<Point> {
|
private final class PolarOrder implements Comparator<Point> {
|
||||||
|
@Override
|
||||||
public int compare(Point p1, Point p2) {
|
public int compare(Point p1, Point p2) {
|
||||||
int dx1 = p1.x - x;
|
int dx1 = p1.x - x;
|
||||||
int dy1 = p1.y - y;
|
int dy1 = p1.y - y;
|
||||||
@ -146,32 +136,23 @@ public class GrahamScan {
|
|||||||
int dy2 = p2.y - y;
|
int dy2 = p2.y - y;
|
||||||
|
|
||||||
if (dy1 >= 0 && dy2 < 0) {
|
if (dy1 >= 0 && dy2 < 0) {
|
||||||
return -1; // q1 above; q2 below
|
return -1; // p1 above p2
|
||||||
} else if (dy2 >= 0 && dy1 < 0) {
|
} else if (dy2 >= 0 && dy1 < 0) {
|
||||||
return +1; // q1 below; q2 above
|
return 1; // p1 below p2
|
||||||
} else if (dy1 == 0 && dy2 == 0) { // 3-collinear and horizontal
|
} else if (dy1 == 0 && dy2 == 0) { // Collinear and horizontal
|
||||||
if (dx1 >= 0 && dx2 < 0) {
|
return Integer.compare(dx2, dx1);
|
||||||
return -1;
|
|
||||||
} else if (dx2 >= 0 && dx1 < 0) {
|
|
||||||
return +1;
|
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return -orientation(Point.this, p1, p2); // Compare orientation
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return -orientation(Point.this, p1, p2); // both above or below
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override of the toString method, necessary to compute the difference
|
* @return A string representation of this point in the format (x, y)
|
||||||
* between the expected result and the derived result
|
|
||||||
*
|
|
||||||
* @return a string representation of any given 2D point in the format (x, y)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "(" + x + ", " + y + ")";
|
return String.format("(%d, %d)", x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user