题目概述:http://coursera.cs.princeton.edu/algs4/assignments/collinear.html
Point类和暴力算法(O(n^4))都很简单,就不写了。
写一个把复杂度降到n^2lg(n)的算法。
感觉写出来的东西很混乱。。
import java.util.ArrayList;
import java.util.Arrays;
import edu.princeton.cs.algs4.Quick3way;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
public class FastCollinearPoints {
private int num = 0; // number of lines
private ArrayList al = null;
public FastCollinearPoints(Point[] points) {
if (points == null)
throw new NullPointerException();
Point[] points1 = new Point[points.length];
System.arraycopy(points, 0, points1, 0, points.length);
al = new ArrayList();
Quick3way.sort(points1); // 保证所有点都在前一个点的右上方。
findDuplicate(points1); // 去除重复点 , 因为算法本身复杂度较高,加 一个线性复杂度的方法。。无所谓了。。
for (int i = 0; i < points1.length - 3; i++) { // 保留最后3个,加上originPoint,正好构成一个4个点的,后面的再遍历也没意义了,还会诱发NullPointerException
if (points1[i] == null)
throw new NullPointerException();
Point[] otherPoints = new Point[points1.length - 1]; // 除了originPoint ,其它点都在这里
Point originPoint = points1[i]; // 从这点画出连接其它点的射线
for (int j = 0, k = 0; k < otherPoints.length; j++, k++) { // initialize otherPoints[]
if (points1[j] == null)
throw new NullPointerException();
if (j < i)
otherPoints[k] = points1[j];
else if (j == i)
k--;
else
otherPoints[k] = points1[j];
}
Arrays.sort(otherPoints, originPoint.slopeOrder()); // 按照斜率排序
int tempNum = 1; // 相同斜率的线条数,一开始肯定有一条,凑够3条就有4个点了,源点 + 3。
double tempSlope = originPoint.slopeTo(otherPoints[0]);
for (int j = 1; j < otherPoints.length; j++) {
if (Double.compare(tempSlope, originPoint.slopeTo(otherPoints[j])) != 0) {
if (tempNum >= 3) {
Point[] temp = new Point[tempNum + 1]; // 把能组成直线的点放到temp里面
temp[0] = originPoint; // put originPoint into temp[0]
for (int k = 1; k <= tempNum; k++) {
temp[k] = otherPoints[j - k];
}
int indexOfMin = findMin(temp); // 找出最小的点,也就是源点
if (temp[indexOfMin] == originPoint) { // 如果最小点不是源点,就说明这个条线之前已经统计过了,直接跳过。
Point head = temp[indexOfMin], tail = temp[indexOfMin]; // 找到头尾
for (int l = 1; l < temp.length; l++) {
if (temp[l].compareTo(head) < 0)
head = temp[l];
if (temp[l].compareTo(tail) > 0)
tail = temp[l];
}
al.add(new LineSegment(head, tail));
num++;
}
}
tempNum = 1; // 还原初始条件,并重设斜率
tempSlope = originPoint.slopeTo(otherPoints[j]);
}
else {
tempNum++;
}
}
if (tempNum >= 3) { // 防止末尾的直线没计入,可能最后斜率没变化就默默结束了
Point[] temp = new Point[tempNum + 1]; // 把能组成直线的点放到temp里面
temp[0] = originPoint; // put originPoint into temp[0]
for (int k = 0; k < tempNum; k++) {
temp[k + 1] = otherPoints[otherPoints.length -1 - k];
}
int indexOfMin = findMin(temp);
if (temp[indexOfMin] == originPoint) {
Point head = temp[indexOfMin], tail = temp[indexOfMin]; // 找到头尾
for (int l = 1; l < temp.length; l++) {
if (temp[l].compareTo(head) < 0)
head = temp[l];
if (temp[l].compareTo(tail) > 0)
tail = temp[l];
}
al.add(new LineSegment(head, tail));
num++;
}
}
}
}
/**
* 寻找最小点
*/
private int findMin(Point[] temp) {
int index = 0;
Point a = temp[0];
for (int i = 1; i < temp.length; i++) {
if (a.compareTo(temp[i]) > 0) {
a = temp[i];
index = i;
}
}
return index;
}
/**
* 排除重复
*/
private void findDuplicate(Point[] points1) {
for (int i = 0; i < points1.length - 1; i++) {
if (points1[i].compareTo(points1[i + 1]) == 0)
throw new IllegalArgumentException();
}
}
public int numberOfSegments() {
// the number of line segments
return num;
}
public LineSegment[] segments() {
return al.toArray(new LineSegment[num]);
}
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
FastCollinearPoints collinear = new FastCollinearPoints(points);
StdOut.println(collinear.segments().length);
for (LineSegment segment : collinear.segments()) {
StdOut.println(segment);
segment.draw();
}
StdDraw.show();
}
}
还是把point也放进来,方便看。。
import java.util.Comparator;
import edu.princeton.cs.algs4.StdDraw;
public class Point implements Comparable {
private final int x; // x-coordinate of this point
private final int y; // y-coordinate of this point
public Point(int x, int y) {
if (x < 0 || x > 32767 || y < 0 || y > 32767)
throw new IllegalArgumentException();
this.x = x;
this.y = y;
}
/**
* Draws this point to standard draw.
*/
public void draw() {
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) {
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.compareTo(that) == 0)
return Double.NEGATIVE_INFINITY; // Same pint
else if (this.x == that.x)
return Double.POSITIVE_INFINITY; // Vertical
else if (this.y == that.y)
return +0.0; // Horizontal
else
return (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 0 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) {
if (this.y > that.y)
return 1;
else if (this.y < that.y)
return -1;
else if (this.y == that.y && this.x > that.x)
return 1;
else if (this.y == that.y && this.x < that.x)
return -1;
else
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 slopeOrder() {
return new SlopeOrder();
}
private class SlopeOrder implements Comparator {
public int compare(Point p1, Point p2) {
double slope1 = slopeTo(p1);
double slope2 = slopeTo(p2);
return Double.compare(slope1, slope2);
}
}
/**
* 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() {
return "(" + x + ", " + y + ")";
}
/**
* Unit tests the Point data type.
*/
public static void main(String[] args) {
/* YOUR CODE HERE */
}
}