programming-examples/java/Data_Structures/GrahamScan.java
2019-11-15 12:59:38 +01:00

108 lines
3.3 KiB
Java

/*************************************************************************
* Compilation: javac GrahamaScan.java
* Execution: java GrahamScan < input.txt
* Dependencies: Point2D.java
*
* Create points from standard input and compute the convex hull using
* Graham scan algorithm.
*
* May be floating-point issues if x- and y-coordinates are not integers.
*
*************************************************************************/
import java.util.Arrays;
import edu.princeton.cs.introcs.StdIn;
import edu.princeton.cs.introcs.StdOut;
public class GrahamScan {
private Stack<Point2D> hull = new Stack<Point2D>();
public GrahamScan(Point2D[] pts) {
// defensive copy
int N = pts.length;
Point2D[] points = new Point2D[N];
for (int i = 0; i < N; i++)
points[i] = pts[i];
// preprocess so that points[0] has lowest y-coordinate; break ties by x-coordinate
// points[0] is an extreme point of the convex hull
// (alternatively, could do easily in linear time)
Arrays.sort(points);
// sort by polar angle with respect to base point points[0],
// breaking ties by distance to points[0]
Arrays.sort(points, 1, N, points[0].POLAR_ORDER);
hull.push(points[0]); // p[0] is first extreme point
// find index k1 of first point not equal to points[0]
int k1;
for (k1 = 1; k1 < N; k1++)
if (!points[0].equals(points[k1])) break;
if (k1 == N) return; // all points equal
// find index k2 of first point not collinear with points[0] and points[k1]
int k2;
for (k2 = k1 + 1; k2 < N; k2++)
if (Point2D.ccw(points[0], points[k1], points[k2]) != 0) break;
hull.push(points[k2-1]); // points[k2-1] is second extreme point
// Graham scan; note that points[N-1] is extreme point different from points[0]
for (int i = k2; i < N; i++) {
Point2D top = hull.pop();
while (Point2D.ccw(hull.peek(), top, points[i]) <= 0) {
top = hull.pop();
}
hull.push(top);
hull.push(points[i]);
}
assert isConvex();
}
// return extreme points on convex hull in counterclockwise order as an Iterable
public Iterable<Point2D> hull() {
Stack<Point2D> s = new Stack<Point2D>();
for (Point2D p : hull) s.push(p);
return s;
}
// check that boundary of hull is strictly convex
private boolean isConvex() {
int N = hull.size();
if (N <= 2) return true;
Point2D[] points = new Point2D[N];
int n = 0;
for (Point2D p : hull()) {
points[n++] = p;
}
for (int i = 0; i < N; i++) {
if (Point2D.ccw(points[i], points[(i+1) % N], points[(i+2) % N]) <= 0) {
return false;
}
}
return true;
}
// test client
public static void main(String[] args) {
int N = StdIn.readInt();
Point2D[] points = new Point2D[N];
for (int i = 0; i < N; i++) {
int x = StdIn.readInt();
int y = StdIn.readInt();
points[i] = new Point2D(x, y);
}
GrahamScan graham = new GrahamScan(points);
for (Point2D p : graham.hull())
StdOut.println(p);
}
}