Programming Assignment 3: Collinear Points
問題描述
Point.java
/******************************************************************************
* Compilation: javac Point.java
* Execution: java Point
* Dependencies: none
*
* An immutable data type for points in the plane.
* For use on Coursera, Algorithms Part I programming assignment.
*
******************************************************************************/
import java.util.Arrays;
import java.util.Comparator;
import edu.princeton.cs.algs4.StdDraw;
public class Point implements Comparable<Point> {
private final int x; // x-coordinate of this point
private final int y; // y-coordinate of this point
/**
* Initializes a new point.
*
* @param x the <em>x</em>-coordinate of the point
* @param y the <em>y</em>-coordinate of the point
*/
public Point(int x, int y) {
/* DO NOT MODIFY */
this.x = x;
this.y = y;
}
/**
* Draws this point to standard draw.
*/
public void draw() {
/* DO NOT MODIFY */
StdDraw.point(x, y);
}
/**
* Draws the line segment between this point and the specified point
* to standard draw.
*
* @param that the other point
*/
public void drawTo(Point that) {
/* DO NOT MODIFY */
StdDraw.line(this.x, this.y, that.x, that.y);
}
/**
* Returns the slope between this point and the specified point.
* Formally, if the two points are (x0, y0) and (x1, y1), then the slope
* is (y1 - y0) / (x1 - x0). For completeness, the slope is defined to be
* +0.0 if the line segment connecting the two points is horizontal;
* Double.POSITIVE_INFINITY if the line segment is vertical;
* and Double.NEGATIVE_INFINITY if (x0, y0) and (x1, y1) are equal.
*
* @param that the other point
* @return the slope between this point and the specified point
*/
public double slopeTo(Point that) {
/* YOUR CODE HERE */
if (this.compareTo(that) == ) return Double.NEGATIVE_INFINITY;
if (this.x == that.x) return Double.POSITIVE_INFINITY;
if (this.y == that.y) return +;
return (this.y - that.y) * / (this.x - that.x);
}
/**
* Compares two points by y-coordinate, breaking ties by x-coordinate.
* Formally, the invoking point (x0, y0) is less than the argument point
* (x1, y1) if and only if either y0 < y1 or if y0 = y1 and x0 < x1.
*
* @param that the other point
* @return the value <tt>0</tt> if this point is equal to the argument
* point (x0 = x1 and y0 = y1);
* a negative integer if this point is less than the argument
* point; and a positive integer if this point is greater than the
* argument point
*/
public int compareTo(Point that) {
/* YOUR CODE HERE */
if (this.x == that.x && this.y == that.y) return ;
if (this.y < that.y || (this.y == that.y && this.x < that.x)) return -;
else return ;
}
/**
* Compares two points by the slope they make with this point.
* The slope is defined as in the slopeTo() method.
*
* @return the Comparator that defines this ordering on points
*/
public Comparator<Point> slopeOrder() {
/* YOUR CODE HERE */
return new BySlopeToThis();
}
private class BySlopeToThis implements Comparator<Point>
{
public int compare(Point a, Point b)
{
Double slopeOfA = a.slopeTo(Point.this);
Double slopeOfB = b.slopeTo(Point.this);
return slopeOfA.compareTo(slopeOfB);
}
}
/**
* Returns a string representation of this point.
* This method is provide for debugging;
* your program should not rely on the format of the string representation.
*
* @return a string representation of this point
*/
public String toString() {
/* DO NOT MODIFY */
return "(" + x + ", " + y + ")";
}
/**
* Unit tests the Point data type.
*/
public static void main(String[] args) {
/* YOUR CODE HERE */
Point p1 = new Point(,);
Point p2 = new Point(,);
Point p3 = new Point(,);
Point p4 = new Point(,);
Point p5 = new Point(,);
Point p6 = new Point(,);
Point p7 = new Point(,);
Point p8 = new Point(,);
Point p9 = new Point(,);
Point p10 = new Point(,);
Point p11 = new Point(,);
Point p12 = new Point(,);
Point[] points = { p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12};
FastCollinearPoints fcp = new FastCollinearPoints(points);
BruteCollinearPoints bcp = new BruteCollinearPoints(points);
for (LineSegment s: bcp.segments())
System.out.print(s.toString());
System.out.println();
for (LineSegment s: fcp.segments())
System.out.print(s.toString());
}
}
BruteCollinearPoints,java
import java.util.Arrays;
public class BruteCollinearPoints {
private int numberOfSegments;
private LineSegment[] segments;
public BruteCollinearPoints(Point[] points) // finds all line segments containing 4 points
{
// Exception 1
if (points == null ) throw new java.lang.IllegalArgumentException();
int N = points.length;
Point[] copy = new Point[N];
// Exception 2
for (int i = ; i < N; i++)
{
if (points[i] == null)
throw new java.lang.IllegalArgumentException();
copy[i] = points[i];
}
// Exception 3
Arrays.sort(copy);
Point previousPoint = copy[];
for (int i = ; i < N; i++)
{
if (copy[i].compareTo(previousPoint) == )
throw new java.lang.IllegalArgumentException();
previousPoint = copy[i];
}
segments = new LineSegment[N*N];
numberOfSegments = ;
for (int i = ; i < N; i++)
{
for (int j = i + ; j < N; j++)
{
for (int k = j + ; k < N; k++)
{
for (int l = k + ; l < N; l++) // ensure uniqueness: (i,j,k,l) comply with certain order
// therefore a segment has one and only one representation
{
if (isCollinear(copy[i], copy[j], copy[k], copy[l]))
store(copy[i], copy[j], copy[k], copy[l]);
}
}
}
}
}
private boolean isCollinear(Point a, Point b, Point c, Point d)
{
return (a.slopeTo(b) == a.slopeTo(c)) && (a.slopeTo(b) == a.slopeTo(d));
}
private void store(Point a, Point b, Point c, Point d)
{
Point[] p = {a, b, c, d};
Arrays.sort(p); // guarantee the order and thus get the beginning and end
LineSegment ls = new LineSegment(p[],p[]);
segments[numberOfSegments++] = ls;
}
public int numberOfSegments() // the number of line segments
{ return numberOfSegments; }
public LineSegment[] segments() // the line segments
{
LineSegment[] result = new LineSegment[numberOfSegments];
for (int i = ; i < numberOfSegments; i++) result[i] = segments[i];
return result;
}
}
FastCollinearPoints.java
import java.util.Arrays;
public class FastCollinearPoints {
private int numberOfSegments;
private LineSegment[] segments;
public FastCollinearPoints(Point[] points) // finds all line segments containing 4 or more points
{
// Exception 1
if (points == null ) throw new java.lang.IllegalArgumentException();
int N = points.length;
Point[] originPoints = new Point[N]; // points to which we calculate slope
Point[] pointsToCheck = new Point[N]; // points we're to check if collinear with the origin
// Exception 2
for (int i = ; i < N; i++)
{
if (points[i] == null)
throw new java.lang.IllegalArgumentException();
originPoints[i] = points[i];
pointsToCheck[i] = points[i];
}
// Exception 3
Arrays.sort(originPoints);
for (int i = ; i < N; i++)
{
if (originPoints[i].compareTo(originPoints[i-]) == )
throw new java.lang.IllegalArgumentException();
}
numberOfSegments = ;
segments = new LineSegment[N*N];
for (int i = ; i < N; i++)
{
Point origin = originPoints[i];
// System.out.println("\nOrigin:"+ origin.toString()); // Debugging Info: print origin
Arrays.sort(pointsToCheck); // necessary: make points in ascending natural order when their slopes are equal
Arrays.sort(pointsToCheck, origin.slopeOrder());
Point start = null; // the first encountered point collinear with the origin, also the 'smallest' one
Point stop = null; // the last encountered point collinear with the origin, also the 'largest' one
int count = ; // number of check points that are collinear with the origin
start = pointsToCheck[];
count = ;
for (int j = ; j < N; j++)
{
// System.out.print(pointsToCheck[j].toString()+" "); // Debugging Info: print point being checked
if (origin.slopeTo(pointsToCheck[j]) != origin.slopeTo(pointsToCheck[j-]))
{
// Situation 1: slope changes, check if we've got 4 or more
if (count >= )
{
stop = pointsToCheck[j-];
checkAndStore(start, stop, origin); // check if unique, if so, store
}
start = pointsToCheck[j];
count = ;
}
else
{
count++;
// Situation 2: slope stay same, but pointToCheck[] ends, check if we've got 4 or more
if (j == N- && count >=)
{
stop = pointsToCheck[j];
checkAndStore(start, stop, origin);
}
}
}
}
}
// A segment can be found N times with each of its N points assigned as 'origin', select
// ONE situation only to ensure uniqueness. Here the choice is only when the origin is
// the 'smallest' point in the segment, that is origin is even 'smaller' than start.
// [Illustration]
//
// (origin)----(start)--()-()-()-()----(stop)
//
// When alias segment is found, just return and do not store it.
private void checkAndStore(Point start, Point stop, Point origin)
{
if (origin.compareTo(start) >= ) return;
else
{
LineSegment ls = new LineSegment(origin, stop);
segments[numberOfSegments++] = ls;
// System.out.print("[" + ls.toString() + "]" ); // Debugging Info: findings
}
}
public int numberOfSegments() // the number of line segments
{ return numberOfSegments; }
public LineSegment[] segments() // the line segments
{
LineSegment[] result = new LineSegment[numberOfSegments];
for (int i = ; i < numberOfSegments; i++) result[i] = segments[i];
return result;
}
}