Coursera普林斯顿大学算法Week3: Pattern Recognition 模式识别

任务连接: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());
        
    }
	
}