Point部分的代码没有什么难度,跟着题目的提示把垂直水平的情况写出来就好了,slopeTo函数写成大的点减去小的点(具体看代码),后面会比较好处理有关slope的问题。
BruteCollinearPoints会有一些要注意的地方:
- 在找CollinearPoints前,先把points按Poin.compareTo从小到大排序。
- first loop 只需要迭代到 len - 2,因为后面只有三个点,second loop,third loop,forth loop同理。
- third loop可以check前两个slope1,slope2是否相等。
- 如果有四个点是Collinear,因为之前已经排序过,所以首点的索引是i,尾点的索引是y。(具体看代码)
FastCollinearPoints
-
同理找CollinearPoints前,排序一遍。
-
根据题目的提示,排序后的每个点p,分别将p左边的点和右边的点按照Point.slopeOrder排序一次,这样子和p构成的slope相同的点就聚在一起了,可以通过遍历一次就可以直到所有和点p形成的lineSegements。
-
有一点比较麻烦的是,在添加lineSegement的时候,需要检查是否已经存在相同的lineSegement。一开始想,添加lineSegements的同时把slope存储进去,然后每次添加时把已有的lineSegements遍历一遍,通过检查slope是否相同来确定要存储的lineSegement是不是子线段。但是这样时间复杂度就会达到O(n*3)。
-
前面将p点左边按照Point.slopeOrder排序,就是为了可以在添lineSegement检查是否存在相同时,使用binary search。这样时间复杂度是O(n*2logn)。
-
存储LineSegments的数组大小我初始化为0,每次添加时增加一个长度,但是这样子会变慢,暂时还没想到其他方法解决。
-
segments() 这个函数评判系统会要求返回一个copy,而不是直接返回本身的数组,因为返回本身数组的话会使得这个数组可以被客户端修改。
下面是我评分98的代码,correctness部分是全对,扣分扣在timing。
Point
import java.util.Comparator;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdOut;
/*
API :
public Point(int x, int y) // constructs the point (x, y)
public void draw() // draws this point
public void drawTo(Point that) // draws the line segment from this point to that point
public String toString() // string representation
public int compareTo(Point that) // compare two points by y-coordinates, breaking ties by x-coordinates
public double slopeTo(Point that) // the slope between this point and that point
public Comparator<Point> slopeOrder() // compare two points by slopes they make with this point
*/
public class Point implements Comparable<Point> {
private final int x; // x-coordinate of this point
private final int y; // y-coordinate of this point
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public void draw() {
StdDraw.point(x, y);
}
public void drawTo(Point that) {
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) {
if (this.x == that.x && this.y == that.y) return Double.NEGATIVE_INFINITY;
else if (this.x == that.x) return Double.POSITIVE_INFINITY;
else if (this.y == that.y) return +0.0;
else {
if (this.compareTo(that) > 0) return ((double) this.y - that.y)/(this.x - that.x);
else return ((double) that.y - this.y)/(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
*/
@Override
public int compareTo(Point that) {
if (this.y < that.y || (this.y == that.y && this.x < that.x)) return -1;
else if (this.x == that.x && this.y == that.y) return 0;
else return 1;
}
/**
* 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() {
return new Order();
}
private class Order implements Comparator<Point> {
@Override
public int compare(Point p1, Point p2) {
double slope1 = slopeTo(p1), slope2 = slopeTo(p2);
if (Double.compare(slope1, slope2) > 0) return 1;
else if (Double.compare(slope1, slope2) < 0) return -1;
else return 0;
}
}
/**
* 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
*/
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
public static void main(String[] args) {
Point p1 = new Point(1, 1);
Point p2 = new Point(23, 500);
StdOut.println(p1.slopeTo(p2));
p1.drawTo(p2);
}
}
BruteCollinearPoints
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdIn;
import java.util.Arrays;
/*
API :
public BruteCollinearPoints(Point[] points) // finds all line segments containing 4 points
public int numberOfSegments() // the number of line segments
public LineSegment[] segments() // the line segments
*/
public class BruteCollinearPoints {
private int num;
private LineSegment[] lines;
public BruteCollinearPoints(Point[] points) {
// Exception handling
if (points == null) throw new IllegalArgumentException();
int len = points.length;
for (int i = 0; i < len; i++) {
if (points[i] == null) throw new IllegalArgumentException();
for (int j = i + 1; j < len; j++) {
if (points[j] == null) throw new IllegalArgumentException();
if (points[i].compareTo(points[j]) == 0) throw new IllegalArgumentException();
}
}
num = 0;
lines = new LineSegment[0];
Point[] tmp = points.clone();
// sort by Point.compareTo
Arrays.sort(tmp);
// first loop
for (int i = 0; (i+3) < len; i++) {
// second loop
for (int j = i+1; (j+2) < len; j++) {
double slope1 = tmp[i].slopeTo(tmp[j]);
// third loop
for (int x = j+1; (x+1) < len; x++) {
double slope2 = tmp[i].slopeTo(tmp[x]);
// check
if (Double.compare(slope1, slope2) != 0)
continue;
// forth loop
for (int y = x+1; y < len; y++) {
double slope3 = tmp[i].slopeTo(tmp[y]);
// if collinear
if (Double.compare(slope1, slope3) == 0) {
// reSize lines if necessary
if (num == lines.length) reSize(lines.length+1);
// save this line and increase num
lines[num++] = new LineSegment(tmp[i], tmp[y]);
}
}
}
}
}
}
public int numberOfSegments() {
return num;
}
public LineSegment[] segments() {
LineSegment[] copy = lines.clone();
return copy;
}
private void reSize(int newSize) {
LineSegment []newl = new LineSegment[newSize];
System.arraycopy(lines, 0, newl, 0, lines.length);
lines = newl;
}
public static void main(String []args) {
// read the n points from a file
int n = StdIn.readInt();
Point[] points = new Point[n];
for (int i = 0; i < n; i++) {
int x = StdIn.readInt();
int y = StdIn.readInt();
points[i] = new Point(x, y);
}
// draw the points
StdDraw.enableDoubleBuffering();
StdDraw.setXscale(0, 32768);
StdDraw.setYscale(0, 32768);
for (Point p : points) {
p.draw();
}
StdDraw.show();
// print and draw the line segments
BruteCollinearPoints collinear = new BruteCollinearPoints(points);
for (LineSegment segment : collinear.segments()) {
StdOut.println(segment);
segment.draw();
}
StdDraw.show();
}
}
FastCollinearPoints
import edu.princeton.cs.algs4.StdDraw;
import java.util.Arrays;
import java.util.Comparator;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdIn;
/*
API:
public FastCollinearPoints(Point[] points) // finds all line segments containing 4 or more points
public int numberOfSegments() // the number of line segments
public LineSegment[] segments() // the line segments
*/
public class FastCollinearPoints {
private int num; // numberOfSegments
private LineSegment[] lines; // line segments
public FastCollinearPoints(Point[] points) {
// Exception handling
if (points == null) throw new IllegalArgumentException();
int len = points.length;
for (int i = 0; i < len; i++) {
if (points[i] == null) throw new IllegalArgumentException();
for (int j = i + 1; j < len; j++) {
if (points[j] == null) throw new IllegalArgumentException();
if (points[i].compareTo(points[j]) == 0) throw new IllegalArgumentException();
}
}
num = 0;
lines = new LineSegment[0];
Point[] ps = points.clone();
// sort by Point.compareTo
Arrays.sort(ps);
// find all line segments
for (int i = 0; i < len-3; i++) {
Point[] tmp = ps.clone();
// sort ps by thinking of p as the origin
Comparator<Point> c = tmp[i].slopeOrder();
Arrays.sort(tmp, i+1, len, c); // sort right part
Arrays.sort(tmp, 0, i, c); // sort left part
/* Check if any 3 (or more) adjacent ps in the sorted order
have equal slopes with respect to p.*/
int flag = 0;
double slope1 = tmp[i].slopeTo(tmp[i+1]);
for (int j = i+1; j < len; j++) {
double slope2 = tmp[i].slopeTo(tmp[j]);
// if the same slope
if (Double.compare(slope1, slope2) == 0) {
flag++; // increase
}
// if not the same slope
else {
// if flag >= 3 then check left part to decide whether to save
if (flag >= 3)
addSegments(tmp, slope1, 0, i-1, i, j-1);
slope1 = slope2;
flag = 1;
}
}
// if flag >= 3 then check left part to decide whether to save
if (flag >= 3)
addSegments(tmp, slope1, 0, i-1, i, len-1);
}
}
public int numberOfSegments() {
return num;
}
public LineSegment[] segments() {
LineSegment[] copy = lines.clone();
return copy;
}
private void reSize(int newSize) {
LineSegment []newl = new LineSegment[newSize];
System.arraycopy(lines, 0, newl, 0, lines.length);
lines = newl;
}
private void addSegments(Point[] tmp, double slope1, int lo, int hi, int i, int j) {
boolean flag = true;
// binary search to check
while (lo <= hi) {
// int mid = (hi + lo)/2; easy to overflow
int mid = lo + (hi - lo)/2;
if (Double.compare(slope1, tmp[i].slopeTo(tmp[mid])) < 0)
hi = mid - 1;
else if (Double.compare(slope1, tmp[i].slopeTo(tmp[mid])) > 0)
lo = mid + 1;
else {
flag = false;
break;
}
}
// save
if (flag) {
// reSize lines if necessary
if (num == lines.length) reSize(lines.length+1);
// save this line and increase num
lines[num++] = new LineSegment(tmp[i], tmp[j]);
}
}
public static void main(String[] args) {
int n = StdIn.readInt();
Point[] points = new Point[n];
for (int i = 0; i < n; i++) {
int x = StdIn.readInt();
int y = StdIn.readInt();
points[i] = new Point(x, y);
}
// draw the points
StdDraw.enableDoubleBuffering();
StdDraw.setXscale(0, 32768);
StdDraw.setYscale(0, 32768);
for (Point p : points) {
p.draw();
}
StdDraw.show();
// print and draw the line segments
FastCollinearPoints collinear = new FastCollinearPoints(points);
for (LineSegment segment : collinear.segments()) {
StdOut.println(segment);
segment.draw();
}
StdDraw.show();
}
}