普林斯顿大学算法 Week3:CollinearPoints 共线模式识别(95分)

FastCollinearPoints的实现中,个人认为最关键的一步是使用Merge Sort对以每个点为出发点的线段进行排序而不能用QuickSort,其原因在与排序的Stability稳定性。MergeSort不会打乱之前因为对Points排序后形成的有序线段组,才能保证后面循环检查线段斜率操作的有效性。

关于排序算法Stability的说明,见《算法(第四版)》第217页的2.5.1.8稳定性一节的讲述

 

 

Points.java

/******************************************************************************
 *  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 edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdOut;

import java.util.Comparator;

public class Point implements Comparable {

    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 x-coordinate of the point
     * @param y the y-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) {
        if (this.equal(that))
            return Double.NEGATIVE_INFINITY;
        if (this.x == that.x)
            return Double.POSITIVE_INFINITY;
        if (this.y == that.y)
            return +0.0;
        return (double) (that.y - this.y) / (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) {
        /* YOUR CODE HERE */
        int result = 1;
        if ((this.y < that.y) || ((this.y == that.y) && (this.x < that.x)))
            result = -1;
        if (this.equal(that))
            result = 0;
        return result;
    }

    /**
     * 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() {
        /* YOUR CODE HERE */
        return new slopeComparator();
    }

    private class slopeComparator implements Comparator {
        public int compare(Point p, Point q) {
            return Double.compare(slopeTo(p), slopeTo(q));
        }
    }

    private boolean equal(Point that) {
        return (this.x == that.x) && (this.y == that.y);
    }


    /**
     * 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 */
        Point p = new Point(1, 1);
        Point p2 = new Point(2, 2);
        Point p3 = new Point(2, 3);

        StdOut.println(p.slopeTo(p2));
        StdOut.println(p.slopeTo(p3));


    }
}

BruteCollinearPoints.java

import java.util.ArrayList;
import java.util.Arrays;

public class BruteCollinearPoints {
    private final ArrayList lineSegments;
    private int count_line;

    public BruteCollinearPoints(Point[] pointsIn) {
        //three exceptions
        if (pointsIn == null)
            throw new IllegalArgumentException();
        int n = pointsIn.length;
        for (Point point : pointsIn)
            if (point == null)
                throw new IllegalArgumentException();
        Point[] points = new Point[n];
        //copy points in pointsIn into private array points
        System.arraycopy(pointsIn, 0, points, 0, n);
        Arrays.sort(points);
        for (int i = 1; i < n; i++)
            if (points[i - 1].compareTo(points[i]) == 0)
                throw new IllegalArgumentException();
        lineSegments = new ArrayList();

        for (int a = 0; a < n - 3; a++) {
            for (int b = a + 1; b < n - 2; b++) {
                double Kab = points[a].slopeTo(points[b]);
                for (int c = b + 1; c < n - 1; c++) {
                    double Kac = points[a].slopeTo(points[c]);
                    if (Kac != Kab) continue;
                    assert Kac == Kab;
                    for (int d = c + 1; d < n; d++) {
                        double Kad = points[a].slopeTo(points[d]);
                        if (Kad != Kab) continue;
                        assert Kad == Kab && Kac == Kab;
                        LineSegment addLine = new LineSegment(points[a], points[d]);
                        lineSegments.add(addLine);
                    }
                }
            }
        }

        count_line = lineSegments.size();

    }

    // the number of line segments
    public int numberOfSegments() {
        return count_line;
    }

    // the line segments
    public LineSegment[] segments() {
        LineSegment[] segments = new LineSegment[count_line];
        int i = 0;
        for (LineSegment Line : lineSegments) {
            segments[i++] = Line;
        }
        return segments;
    }


    //main
    public static void main(String[] args) {

    }

}

FastCollinearPoints.java

import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.Merge;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdOut;

import java.util.ArrayList;
import java.util.Arrays;

public class FastCollinearPoints {
    private ArrayList lineSegments;
    private ArrayList linesArraylist;
    private int count_line;

    public FastCollinearPoints(Point[] pointsIn) {
        linesArraylist = new ArrayList();
        //three exceptions
        if (pointsIn == null)
            throw new IllegalArgumentException();
        int n = pointsIn.length;
        for (Point point : pointsIn)
            if (point == null)
                throw new IllegalArgumentException();
        Point[] points = new Point[n];

        //copy points in pointsIn into private array points
        System.arraycopy(pointsIn, 0, points, 0, n);
        Arrays.sort(points);
        for (int i = 1; i < n; i++) {
            if (points[i - 1].compareTo(points[i]) == 0) {
                throw new IllegalArgumentException();
            }
        }

        for (int i = 0; i < n - 1; i++) {
            line[] lines = new line[n - 1 - i];
            int sz = 0;
            for (int j = i + 1; j < n; j++) {
                line tempLine = new line(points[i], points[j]);
                lines[sz++] = tempLine;
            }

            if (lines.length < 3)
                continue;
            Merge.sort(lines);
//            StdOut.println("以点" + points[i] + "为起点的线总共有" + lines.length + "条");
//            for (line temp : lines) {
//                StdOut.println(temp + ",其斜率为:" + temp.slope);
//            }
//
//            StdOut.println();
//            StdOut.println();


            int pa = 0, pb = 1;
            while (pa < sz && pb < sz) {
                if (lines[pa].slope == lines[pb].slope) {
                    pb++;
                    if (pb == sz && pb - pa >= 3) {
                        line temp_Line = new line(lines[pa].startPoint, lines[pb - 1].endPoint);
                        if (!isExisted(lines[pb - 1].endPoint, temp_Line.slope)) {
                            linesArraylist.add(temp_Line);
                            //StdOut.println("添加了线段" + temp_Line);
                        }
                    }

                    continue;
                }
                if ((pb - pa) < 3) {
                    pa = pb;
                    pb++;
                } else {
                    line temp_Line = new line(lines[pa].startPoint, lines[pb - 1].endPoint);
                    if (!isExisted(lines[pb - 1].endPoint, temp_Line.slope)) {
                        linesArraylist.add(temp_Line);
                        //StdOut.println("添加了线段" + temp_Line);
                    }

                    pa = pb;
                    pb++;
                }
            }

//
//            StdOut.println();
//            StdOut.println();
//            StdOut.println();

        }

        lineSegments = new ArrayList();
        for (line l : linesArraylist) {
            LineSegment lineSegment = new LineSegment(l.startPoint, l.endPoint);
            lineSegments.add(lineSegment);

        }

        count_line = lineSegments.size();
//               int lineNum = (N * (N - 1)) / 2;
//        int index = 0;
//        //把所有的两点线段全都加到lines中,lines用于存储所有的线段
//        for (int i = 0; i < N - 1; i++) {
//            for (int j = i + 1; j < N; j++) {
//                line tempLine = new line(points[i], points[j]);
//                lines[index++] = tempLine;
//            }
//        }
//        assert index == lineNum - 1;

//        //将所有线段按照斜率进行快排(不需要考虑稳定性)
//        assert lines != null;
//        Quick.sort(lines);
//        //将所有线段按照名称进行归并排序
    }

    private boolean isExisted(Point end, double slope) {
        boolean result = false;
        for (line l : linesArraylist) {
            if ((end.compareTo(l.endPoint) == 0) && (Double.compare(slope, l.slope) == 0)) {
                result = true;
                break;
            }
        }
        return result;
    }


    //由两个点组成的线段,提供按照斜率和按照起始点两种排序方式
    private static class line implements Comparable {

        public final Point startPoint;
        public final Point endPoint;
        public double slope;

        public line(Point s, Point e) {
            startPoint = s;
            endPoint = e;
            slope = s.slopeTo(e);
        }

        @Override
        public int compareTo(line o) {
            return Double.compare(this.slope, o.slope);
        }

        @Override
        public String toString() {
            return startPoint + " -> " + endPoint;
        }
    }


    public int numberOfSegments() {
        return count_line;
    }

    public LineSegment[] segments() {
        LineSegment[] segments = new LineSegment[count_line];
        int i = 0;
        for (LineSegment Line : lineSegments) {
            segments[i++] = Line;
        }
        return segments;
    }

    //main
    public static void main(String[] args) {
        In in = new In("src/input8.txt");
        int n = in.readInt();
        StdOut.println("total " + n + " points");
        Point[] points = new Point[n];
        for (int i = 0; i < n; i++) {
            int x = in.readInt();
            int y = in.readInt();
            StdOut.println("(" + x + "," + y + ")");
            points[i] = new Point(x, y);
        }
        //draw the points
        StdDraw.enableDoubleBuffering();
        StdDraw.setXscale(0, 32768);
        StdDraw.setYscale(0, 32768);
        StdDraw.setPenColor(StdDraw.RED);
        StdDraw.setPenRadius(0.01);
        for (Point p : points) {
            p.draw();
        }
        StdDraw.show();
        // print and draw the line segments
        FastCollinearPoints collinear = new FastCollinearPoints(points);
        StdOut.println(collinear.numberOfSegments());
        for (LineSegment segment : collinear.segments()) {
            StdOut.println(segment);
            segment.draw();
        }
        StdDraw.show();

//        Point test1 = new Point(1000, 21);
//        Point test2 = new Point(1000, 2000);
//        Point test3 = new Point(1000, 3000);
//        StdOut.println(test1.slopeTo(test2) == test1.slopeTo(test3));
        //上面几行的输出是true
    }
}

 

你可能感兴趣的:(算法)