【Coursera】编程题 Collinear Points - Algorithms Part1, Week3

题目概述: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 */
    }
}




你可能感兴趣的:(算法,&&,数据结构)