Week 3 編程作業:Collinear Points

編程作業: Collinear Points

100分關鍵是FastCollinearPoints在判斷線段重複時不使用Set或Sort,而是只有當前點是當前線段的左下端點時才加入

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.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 (that == null)
			throw new java.lang.NullPointerException();

		if (that.x == this.x)
			return that.y == this.y ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
		return that.y == this.y ? 0 : (that.y - this.y) * 1.0 / (that.x - this.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 (that == null)
			throw new java.lang.NullPointerException();

		int i = this.y - that.y;
		return i == 0 ? this.x - that.x : i;
	}

	/**
	 * 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 Comparator<Point>() {
			@Override
			public int compare(Point p1, Point p2) {
				if (p1 == null || p2 == null)
					throw new java.lang.NullPointerException();
				return Double.compare(slopeTo(p1), slopeTo(p2));
			}
		};
	}

	/**
	 * 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) {

	}
}

BruteCollinearPoints.java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class BruteCollinearPoints {

	private final List<LineSegment> list;

	public BruteCollinearPoints(Point[] points) {
		if (points == null)
			throw new java.lang.IllegalArgumentException();

		checkPoints(points);
		this.list = new ArrayList<>();
		if (points.length >= 4)
			brute(points);
	}

	private void brute(Point[] points) {
		for (int a = 0; a < points.length - 3; a++) {
			for (int b = a + 1; b < points.length - 2; b++) {
				for (int c = b + 1; c < points.length - 1; c++) {
					for (int d = c + 1; d < points.length; d++) {
						if (collinear(points[a], points[b], points[c], points[d]))
							addLineSegment(points[a], points[b], points[c], points[d]);
					}
				}
			}
		}
	}

	private static boolean collinear(Point a, Point b, Point c, Point d) {
		final double slopeAB = a.slopeTo(b);
		return slopeAB == a.slopeTo(c) && slopeAB == a.slopeTo(d);
	}

	private void addLineSegment(Point... ps) {
		Arrays.sort(ps);
		this.list.add(new LineSegment(ps[0], ps[ps.length - 1]));
	}

	private static void checkPoints(Point... ps) {
		for (Point p : ps) {
			if (p == null)
				throw new java.lang.IllegalArgumentException();
		}
		for (int i = 0; i < ps.length - 1; i++) {
			for (int j = i + 1; j < ps.length; j++) {
				if (ps[i].compareTo(ps[j]) == 0)
					throw new java.lang.IllegalArgumentException();
			}
		}
	}

	public int numberOfSegments() {
		return this.list.size();
	}

	public LineSegment[] segments() {
		return this.list.toArray(new LineSegment[0]);
	}

}

FastCollinearPoints.java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class FastCollinearPoints {

	private final List<LineSegment> list;
	private final ComputePoint[] cps;

	public FastCollinearPoints(Point[] points) {
		checkNullPoints(points);

		Point[] clone = new Point[points.length];
		System.arraycopy(points, 0, clone, 0, points.length);
		checkRepeatPoints(clone);

		this.list = new ArrayList<>();

		this.cps = new ComputePoint[points.length];
		for (int i = 0; i < points.length; i++)
			this.cps[i] = new ComputePoint();

		for (int i = 0; i < points.length - 3; i++)
			fast(clone, i);
	}

	private static void checkNullPoints(Point[] ps) {
		if (ps == null)
			throw new java.lang.IllegalArgumentException();

		for (Point p : ps) {
			if (p == null)
				throw new java.lang.IllegalArgumentException();
		}
	}

	private static void checkRepeatPoints(Point[] ps) {
		Arrays.sort(ps);
		for (int i = 1; i < ps.length; i++) {
			if (ps[i - 1].compareTo(ps[i]) == 0)
				throw new java.lang.IllegalArgumentException();
		}
	}

	private void fast(Point[] ps, int cur) {
		Point curP = ps[cur];

		for (int i = 0; i < ps.length; i++) {
			this.cps[i].p = ps[i];
			this.cps[i].slope = curP.slopeTo(ps[i]);
		}

		Arrays.sort(cps, ComputePoint.SORT_BY_SLOPE);

		int c = 1;
		for (int i = 1; i < ps.length; i++) {
			if (cps[i - 1].slope == cps[i].slope) {
				c++;
				continue;
			}
			if (c >= 3) {
				addLine(curP, i - c, c);
			}
			c = 1;
		}
		if (c >= 3) {
			int i = ps.length;
			addLine(curP, i - c, c);
		}
	}

	private void addLine(Point curP, int cpsS, int len) {
		Point min = min(curP, cps[cpsS].p);
		Point max = max(curP, cps[cpsS + len - 1].p);

		if (curP != min)
			return;

		this.list.add(new LineSegment(min, max));
	}

	private static Point min(Point a, Point b) {
		return a.compareTo(b) > 0 ? b : a;
	}

	private static Point max(Point a, Point b) {
		return a.compareTo(b) < 0 ? b : a;
	}

	private static class ComputePoint {
		Point p;
		double slope;

		static final Comparator<ComputePoint> SORT_BY_SLOPE = new Comparator<FastCollinearPoints.ComputePoint>() {
			@Override
			public int compare(ComputePoint o1, ComputePoint o2) {
				return Double.compare(o1.slope, o2.slope);
			}
		};
		static final Comparator<ComputePoint> SORT_BY_P = new Comparator<FastCollinearPoints.ComputePoint>() {
			@Override
			public int compare(ComputePoint o1, ComputePoint o2) {
				return o1.p.compareTo(o2.p);
			}
		};
	}

	public int numberOfSegments() {
		return this.list.size();
	}

	public LineSegment[] segments() {
		return this.list.toArray(new LineSegment[0]);
	}

}