任务链接:http://coursera.cs.princeton.edu/algs4/assignments/collinear.html
本次任务主要是比较器Comparator的使用。
在暴力求解时,可以不必在最后再对三个斜率进行比较,先比较两个可以有效减少算法时间。
在快速求解时,借助了一个内部类构成的数组存储点和斜率,并将这个数组按斜率进行排序,相同斜率的即为同一直线。
在暴力和快速求解中,不能使用动态数组进行存储线段,不然在最坏情况下时间复杂度为O(n),本例子中借助链表先进行存储,再转化到数组中。
但是,通过提交发现,仍有大量测试案例无法通过,勉强及格,暂时没找到原因,后面再找吧。
/******************************************************************************
* 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());
}
}