任务连接:http://coursera.cs.princeton.edu/algs4/assignments/collinear.htmlhtml
本次任务主要是比较器Comparator的使用。java
在暴力求解时,能够没必要在最后再对三个斜率进行比较,先比较两个能够有效减小算法时间。算法
在快速求解时,借助了一个内部类构成的数组存储点和斜率,并将这个数组按斜率进行排序,相同斜率的即为同一直线。数组
在暴力和快速求解中,不能使用动态数组进行存储线段,否则在最坏状况下时间复杂度为O(n),本例子中借助链表先进行存储,再转化到数组中。less
可是,经过提交发现,仍有大量测试案例没法经过,勉强及格,暂时没找到缘由,后面再找吧。ide
/****************************************************************************** * 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.In; import edu.princeton.cs.algs4.StdDraw; import edu.princeton.cs.algs4.StdOut; 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.y == that.y && this.x != that.x) // 水平线,斜率为0 return +0.0; if (this.x == that.x && this.y != that.y) // 垂直线,斜率为正无穷 return Double.POSITIVE_INFINITY; if (this.y == that.y && this.x == that.x) // 同一点,斜率为负无穷 return Double.NEGATIVE_INFINITY; return (that.y - this.y)*1.0/(that.x - this.x); // 斜率, (y1-y0)/(x1-x0) } /** * 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) // 先比较y,再比较x { /* YOUR CODE HERE */ if (this.y < that.y) return -1; if (this.y > that.y) return +1; if (this.x < that.x) return -1; if (this.x > that.x) return +1; return 0; } /** * 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 SlperOrder(this); } private class SlperOrder implements Comparator<Point> // 构造比较器 { private final Point p0; public SlperOrder(Point invokePoint) { this.p0 = invokePoint; } public int compare(Point pPoint, Point qPoint) { return Double.compare(p0.slopeTo(pPoint), p0.slopeTo(qPoint)); } } /** * 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 */ // read the n points from a file In in = new In(args[0]); int n = in.readInt(); Point[] points = new Point[n]; for (int i = 0; i < n; i++) { int x = in.readInt(); int y = in.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(); } }
import java.util.Arrays; import java.util.Comparator; public class FastCollinearPoints { private static int count; // 线段个数 private LineNode first; // 线段链表初始化 private LineNode last; // 当前线段元素 private class LineNode // 使用链表存储线段 { LineSegment value; LineNode next; } private class PiontSloper { Point point; double slope; } public FastCollinearPoints(Point[] points) // finds all line segments containing 4 or more points { count = 0; Point []clone = throwFuntion(points); PiontSloper []pointSlopes = new PiontSloper[clone.length]; for (int i = 0; i < clone.length; i++) // i至少为倒第四个点 { for (int j = 0; j < clone.length; j++) // j从i后的点开始查找 { // 记录j点关于i点的斜率 pointSlopes[j] = new PiontSloper(); pointSlopes[j].slope = clone[i].slopeTo(clone[j]); pointSlopes[j].point = clone[j]; } // 对关于点i的斜率从a[i+1]到a[length-1]进行排序 Arrays.sort(pointSlopes, new ComPointSlope()); int current = 0; // 当前比较值 int num = 0; // 斜率重复的个数 for (int k = 0; k < pointSlopes.length; k++) { if (Double.compare(pointSlopes[current].slope, pointSlopes[k].slope) == 0) { num++; } else { if (num >= 3 && pointSlopes[current].point.compareTo(clone[i]) == 1) // 存在4个以上的点构成一条直线 { LineNode newNode = new LineNode(); // 添加新的线段元素 newNode.value = new LineSegment(clone[i], pointSlopes[k-1].point); newNode.next = null; if (last == null) // 原链表中无元素 { last = newNode; first = last; } else // 原链表中有元素 { last.next = newNode; last = newNode; } count++; // 计数 } current = k; num = 1; } } } } // 对数组进行异常检测,并返回点集的拷贝排序数组clone[] private Point[] throwFuntion(Point[] points) { if (points == null) // 判断数组是否为空 throw new java.lang.IllegalArgumentException("The Point[] is null"); Point [] clone = new Point[points.length]; // 避免破坏原数组,拷贝原数组值 for (int i = 0; i < points.length; i++) { if (points[i] == null) // 判断是否含有空点 throw new java.lang.IllegalArgumentException("There has any point in the array is null"); clone[i] = points[i]; // 拷贝原数组值 } Arrays.sort(clone); // 对点集进行排序,便于后面的判断两个点是否重复 for (int i = 0, j = 1; j < clone.length; i++, j++) { if (clone[i].compareTo(clone[j]) == 0) // 判断是否有两个点重复 throw new java.lang.IllegalArgumentException("The Point[] contain a repeated point"); } return clone; } private class ComPointSlope implements Comparator<PiontSloper> { @Override public int compare(PiontSloper arg0, PiontSloper arg1) { return Double.compare(arg0.slope, arg1.slope); } } public int numberOfSegments() // the number of line segments { return count; } public LineSegment[] segments() // the line segments { LineSegment[] lineSegments = new LineSegment[count]; LineNode current = first; for (int i = 0; i < lineSegments.length; i++) { lineSegments[i] = current.value; current = current.next; } return lineSegments; } public static void main(String[] args) { Point[] points = new Point[15]; points[0] = new Point(1, 1); points[1] = new Point(3, 2); points[2] = new Point(6, 6); points[3] = new Point(1, 0); points[4] = new Point(4, 6); points[5] = new Point(5, 1); points[6] = new Point(7, 1); points[7] = new Point(4, 3); points[8] = new Point(8, 5); points[9] = new Point(3, 1); points[10] = new Point(6, 4); points[11] = new Point(8, 7); points[12] = new Point(9, 8); points[13] = new Point(9, 3); points[14] = new Point(5, 5); FastCollinearPoints fastCollinearPoints = new FastCollinearPoints(points); System.out.println(fastCollinearPoints.numberOfSegments()); LineSegment []lineSegment = fastCollinearPoints.segments(); for (int i = 0; i < lineSegment.length; i++) { System.out.println(lineSegment[i].toString()); } System.out.println(Double.compare(0.0, 0.0)); } }
import java.util.Arrays; public class BruteCollinearPoints { private static int count; // 线段个数 private LineNode first; // 线段链表初始化 private LineNode last; // 当前线段元素 private class LineNode // 使用链表存储线段 { LineSegment value; LineNode next; } public BruteCollinearPoints(Point[] points) // 暴力求解包含四个点的线段 { count = 0; if (points == null) // 判断数组是否为空 throw new java.lang.IllegalArgumentException("The Point[] is null"); Point [] clone = new Point[points.length]; // 避免破坏原数组,拷贝原数组值 for (int i = 0; i < points.length; i++) { if (points[i] == null) // 判断是否含有空点 throw new java.lang.IllegalArgumentException("There has any point in the array is null"); clone[i] = points[i]; // 拷贝原数组值 } Arrays.sort(clone); // 对点集进行排序,便于后面的判断两个点是否重复 for (int i = 0; i < clone.length-1; i++) { if (clone[i].compareTo(clone[i+1]) == 0) // 判断是否有两个点重复 throw new java.lang.IllegalArgumentException("The Point[] contain a repeated point"); } for (int i = 0; i < clone.length-3; i++) { for (int j = i+1; j < clone.length-2; j++) { double sloperij = clone[i].slopeTo(clone[j]); for (int k = j+1; k < clone.length-1; k++) { double sloperik = clone[i].slopeTo(clone[k]); if (Double.compare(sloperij, sloperik) != 0) continue; for (int n = k+1; n < clone.length; n++) { double sloperin = clone[i].slopeTo(clone[n]); if (Double.compare(sloperij, sloperik) == 0 && Double.compare(sloperij, sloperin) == 0) //四点共线 { LineNode newNode = new LineNode(); // 添加新的线段元素 newNode.value = new LineSegment(clone[i], clone[n]); newNode.next = null; if (last == null) // 原链表中无元素 { last = newNode; first = last; } else // 原链表中有元素 { last.next = newNode; last = newNode; } count++; // 计数 } } } } } } public int numberOfSegments() // the number of line segments { return count; } public LineSegment[] segments() // the line segments { LineSegment[] lineSegments = new LineSegment[count]; LineNode current = first; for (int i = 0; i < count; i++) { lineSegments[i] = current.value; current = current.next; } return lineSegments; } public static void main(String[] args) { Point[] points = new Point[15]; points[0] = new Point(1, 1); points[1] = new Point(3, 2); points[2] = new Point(6, 6); points[3] = new Point(1, 0); points[4] = new Point(4, 6); points[5] = new Point(5, 1); points[6] = new Point(7, 1); points[7] = new Point(4, 3); points[8] = new Point(8, 5); points[9] = new Point(3, 1); points[10] = new Point(6, 4); points[11] = new Point(8, 7); points[12] = new Point(9, 8); points[13] = new Point(9, 3); points[14] = new Point(5, 5); System.out.println(points[0].slopeTo(points[1])); BruteCollinearPoints bb = new BruteCollinearPoints(points); LineSegment []lineSegment = bb.segments(); for (int i = 0; i < lineSegment.length; i++) { System.out.println(lineSegment[i].toString()); } System.out.println(bb.numberOfSegments()); } }