Coursera Algorithms第三周编程任务

Programming Assignment 3: Pattern Recognition

Write a program to recognize line patterns in a given set of points.

Algorithms 第三周的编程任务,主要目的是编写一个Point类来代表平面上的点,一个BruteCollinearPoints类来计算所有包含四个点的线段,一个FastCollinearPoints来计算所有包含4个及四个以上点的线段。

课程给出的四个数据类型说明(API):

Point data type. Create an immutable data type Point,(use the data type Point.java), that represents a point in the plane by implementing the following API:

public class Point implements Comparable<Point> {
   public Point(int x, int y)                         // constructs the point (x, y)

   public   void draw()                               // draws this point
   public   void drawTo(Point that)                   // draws the line segment from this point to that point
   public String toString()                           // string representation

   public               int compareTo(Point that)     // compare two points by y-coordinates, breaking ties by x-coordinates
   public            double slopeTo(Point that)       // the slope between this point and that point
   public Comparator<Point> slopeOrder()              // compare two points by slopes they make with this point
}

Line segment data type. To represent line segments in the plane, use the data type LineSegment.java, which has the following API:

public class LineSegment {
   public LineSegment(Point p, Point q)        // constructs the line segment between points p and q
   public   void draw()                        // draws this line segment
   public String toString()                    // string representation
}

Brute force. Write a program BruteCollinearPoints.java that examines 4 points at a time and checks whether they all lie on the same line segment, returning all such line segments. To check whether the 4 points p, q, r, and s are collinear, check whether the three slopes between p and q, between p and r, and between p and s are all equal.

public class BruteCollinearPoints {
   public BruteCollinearPoints(Point[] points)    // finds all line segments containing 4 points
   public           int numberOfSegments()        // the number of line segments
   public LineSegment[] segments()                // the line segments
}

Fast . Write a program FastCollinearPoints.java that implements this algorithm.

public class FastCollinearPoints {
   public FastCollinearPoints(Point[] points)     // finds all line segments containing 4 or more points
   public           int numberOfSegments()        // the number of line segments
   public LineSegment[] segments()                // the line segments
} 

方案

Point 类没什么可以说的,照着API的需求写就可以了。

代码实现

import edu.princeton.cs.algs4.StdDraw;

import java.util.Comparator;

public class Point implements Comparable<Point> {

    private final int x;
    private final int y;

    /**
     * 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) {
        double diffX = (double) (that.x - this.x);
        double diffY = (double) (that.y - this.y);
        if (diffY == 0) {
            return diffX == 0 ? Double.NEGATIVE_INFINITY : +0.0;
        } else if (diffX == 0) {
            return Double.POSITIVE_INFINITY;
        } else {
            return diffY / diffX;
        }
    }

    /**
     * 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
     */
    @Override
    public int compareTo(Point that) {
        if (this.y > that.y) {
            return +1;
        } else if (this.y < that.y) {
            return -1;
        } else {
            return Integer.compare(this.x, that.x);
        }
    }

    /**
     * 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() {
        return new BySlope();
    }

    /**
     * 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
     */
    @Override
    public String toString() {
        /* DO NOT MODIFY */
        return "(" + x + ", " + y + ")";
    }

    private class BySlope implements Comparator<Point> {
        @Override
        public int compare(Point o1, Point o2) {
            Double slope1 = Point.this.slopeTo(o1);
            Double slope2 = Point.this.slopeTo(o2);
            return slope1.compareTo(slope2);
        }
    }
}

BruteCollinearPoints :只需要进行四层for循环嵌套,然后进行判断就可以了。

实现代码

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

public class BruteCollinearPoints {
    private final ArrayList<LineSegment> lineSegments;

    /**
     * Finds all line segments containing 4 points.
     *
     * @throws IllegalArgumentException the argument to the constructor is null,
     *                              if any point in the array is null, or if the
     *                              argument to the constructor contains a
     *                              repeated point.
     */
    public BruteCollinearPoints(Point[] points) {
        isLegal(points);
        Point[] pointsCopy = Arrays.copyOf(points, points.length);
        lineSegments = new ArrayList<>();
        Arrays.sort(pointsCopy);

        for (int i = 0; i < pointsCopy.length - 3; i++) {
            for (int j = i + 1; j < pointsCopy.length - 2; j++) {
                for (int k = j + 1; k < pointsCopy.length - 1; k++) {
                    if (!isCollinear(pointsCopy, i, j, k)) {
                        continue;
                    }
                    for (int m = k + 1; m < pointsCopy.length; ++m) {
                        if (isCollinear(pointsCopy, i, k, m)) {
                            lineSegments.add(new LineSegment(pointsCopy[i], pointsCopy[m]));
                        }
                    }
                }
            }
        }
    }

    private boolean isCollinear(Point[] points, int i, int j, int k) {
        double firstSlope = points[i].slopeTo(points[j]);
        double secondSlope = points[i].slopeTo(points[k]);
        return firstSlope == secondSlope;
    }

    private void isLegal(Point[] points) {
        if (points == null) {
            throw new java.lang.IllegalArgumentException();
        }

        for (Point point : points) {
            if (point == null) {
                throw new IllegalArgumentException();
            }
        }

        for (int i = 0; i < points.length - 1; i++) {
            for (int j = i + 1; j < points.length; j++) {
                if (points[i].compareTo(points[j]) == 0) {
                    throw new IllegalArgumentException();
                }
            }
        }
    }

    /**
     * Returns the number of line segments.
     *
     * @return the number of line segments.
     */
    public int numberOfSegments() {
        return lineSegments.size();
    }

    /**
     * Returns a LineSegment array.
     *
     * @return a LineSegment array.
     */
    public LineSegment[] segments() {
        return lineSegments.toArray(new LineSegment[numberOfSegments()]);
    }
}

FastCollinearPoints:思想如下,

​ 我们选中一个点作为起始点,因为API要求我们不能存储一条线段的子线段,所以我们需要判断当前线段是否为之前线段的子线段。用数组存储它和它之前的点表示的斜率,如果起始点与它之后的点构成四点及以上的一线,那么可以通过判断起始点和它之后的点代表的斜率是否与之前的斜率存在相等的情况来得出当前得出的线段是否是之前得出的线段的子线段。

005HCBEwly1fsolpbz1zaj30gz0h2jry.jpg

如上图情况一是不能添加到线段集合的,而情况二则是可以的。

我们存储当前点之前的斜率时,需要对数组进行排序,目的是为了方便之后的查找。

实现代码

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


public class FastCollinearPoints {
    private final ArrayList<LineSegment> lineSegments;

    /**
     * Finds all line segments containing 4 pointsCopy or more pointsCopy.
     *
     * @throws IllegalArgumentException if the argument to the constructor is null if any
     *                                  point in the array is null, or if the argument to
     *                                  the constructor contains a repeated point.
     */
    public FastCollinearPoints(Point[] points) {
        isLegal(points);
        Point[] pointsCopy = Arrays.copyOf(points, points.length);
        Arrays.sort(pointsCopy);
        lineSegments = new ArrayList<>();

        for (int i = 0; i < pointsCopy.length - 3; i++) {
            Point startPoint = pointsCopy[i];
            double[] preSlopes = new double[i];
            Point[] nextPoints = new Point[pointsCopy.length - i - 1];

            for (int j = 0; j < i; j++) {
                preSlopes[j] = startPoint.slopeTo(pointsCopy[j]);
            }

            for (int j = 0; j < pointsCopy.length - i - 1; j++) {
                nextPoints[j] = pointsCopy[i + j + 1];
            }
            //for binary search
            Arrays.sort(preSlopes);
            // sort after point by slope
            Arrays.sort(nextPoints, startPoint.slopeOrder());
            findLineSegments(preSlopes, startPoint, nextPoints);
        }
    }

    private void findLineSegments(double[] preSlopes, Point startPoint, Point[] nextPoints) {
        double currentSlope;
        double beforeSlope = Double.NEGATIVE_INFINITY;
        int countRepeat = 1;

        for (int i = 0; i < nextPoints.length; i++) {
            currentSlope = startPoint.slopeTo(nextPoints[i]);
            if (beforeSlope != currentSlope) {
                //beforeSlope != currentSlope and countRepeat>= 3
                if (countRepeat >= 3 && !isSubLine(beforeSlope, preSlopes)) {
                    lineSegments.add(new LineSegment(startPoint, nextPoints[i - 1]));
                }
                countRepeat = 1;
            } else {
                countRepeat++;
            }
            beforeSlope = currentSlope;
        }
        //record the rest of the situation.
        if (countRepeat >= 3 && !isSubLine(beforeSlope, preSlopes)) {
            lineSegments.add(new LineSegment(startPoint, nextPoints[nextPoints.length - 1]));
        }
    }

    private void isLegal(Point[] points) {
        if (points == null) {
            throw new java.lang.IllegalArgumentException();
        }

        for (Point point : points) {
            if (point == null) {
                throw new IllegalArgumentException();
            }
        }

        for (int i = 0; i < points.length - 1; i++) {
            for (int j = i + 1; j < points.length; j++) {
                if (points[i].compareTo(points[j]) == 0) {
                    throw new IllegalArgumentException();
                }
            }
        }
    }

    private boolean isSubLine(double tempSlope, double[] beforeSlope) {
        int lo = 0;
        int hi = beforeSlope.length - 1;
        // use binary search
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            if (tempSlope < beforeSlope[mid]) {
                hi = mid - 1;
            } else if (tempSlope > beforeSlope[mid]) {
                lo = mid + 1;
            } else {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the number of line segments.
     *
     * @return the number of line segments.
     */
    public int numberOfSegments() {
        return lineSegments.size();
    }

    /**
     * Returns a LineSegment array.
     *
     * @return a LineSegment array.
     */
    public LineSegment[] segments() {
        return lineSegments.toArray(new LineSegment[numberOfSegments()]);
    }
}

转载于:https://www.cnblogs.com/revc/p/9229293.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值