Algorithm I assignment Collinear

这本来应该是第三周的作业,但是由于其他作业逼近deadline,暂时推后了一周完成。

这周的assignment大大提高了我对这门课的看法,不得不说,Algorithms这门课的assignment部分设计得很好。为什么好?个人认为有以下几点:

  1. 程序要求解耦
  2. 循序渐进,这周的作业不允许使用hashcode()
  3. 输入变量不允许被程序改变

能做到1、3两点,在工程中也是高质量代码的体现


 

题目很简单,即给出一个点集,找所有出任意四点或以上共线的线段,将该线段的端点new 一个segment()对象存起来。看起来很简单,实际上到处都是坑TAT

第一部分是用brute force的方法做,由于4个及以上的点才是共线,这里要求中提出为了简化,brute方法不考虑5个及以上的点。所以既是对点集进行4层循环即可,时间复杂度是O(n4)。

但是这里需要注意的是,输入的点集,不能被直接操作,必须被赋值为拷贝才行,否则,外界如果更改了点集中的点,则会影响结果。这一点还说明了,java都是值传递,传进来的是一个数组引用的copy。

另外辩解条件必须要注意,其实挺容易出错的。

 1 public class BruteCollinearPoints {
 2     // finds all line segments containing 4 points
 3     private int N;
 4     private ArrayList<LineSegment> segment = new ArrayList<LineSegment>();
 5 
 6     public BruteCollinearPoints(Point[] points) {
 7         if (points == null)
 8             throw new java.lang.NullPointerException();
 9         N = 0;
10         Point[] copy = new Point[points.length];
11         for (int i = 0; i < points.length; i++) {
12             copy[i] = points[i];
13         }
14         Arrays.sort(copy);
15         for (int i = 0; i < copy.length - 1; i++) {
16             if (copy[i].compareTo(copy[i + 1]) == 0) {
17                 throw new java.lang.IllegalArgumentException();
18             }
19         }
20         for (int i = 0; i < copy.length - 3; i++) {
21             for (int j = i + 1; j < copy.length - 2; j++) {
22                 double slope1 = copy[i].slopeTo(copy[j]);
23                 for (int k = j + 1; k < copy.length - 1; k++) {
24                     double slope2 = copy[i].slopeTo(copy[k]);
25                     if (slope1 != slope2)
26                         continue;
27                     int temp = 0;
28                     for (int l = k + 1; l < copy.length; l++) {
29                         double slope3 = copy[i].slopeTo(copy[l]);
30                         if (slope1 == slope3)
31                             temp = l;
32                         if ((l == copy.length - 1) && (temp != 0)) {
33                             N++;
34                             segment.add(new LineSegment(copy[i], copy[temp]));
35                         }
36                     }
37                 }
38             }
39         }
40     }
41 
42     // the number of line segments
43     public int numberOfSegments() {
44         return N;
45     }
46 
47     // the line segments
48     public LineSegment[] segments() {
49         LineSegment[] results = new LineSegment[N];
50         for (int i = 0; i < N; i++) {
51             results[i] = segment.get(i);
52         }
53         return results;
54     }
55 
56     public static void main(String[] args) {
57 
58         // read the N points from a file
59         // In in = new In(args[0]);
60         In in = new In("./collinear/rs1423.txt");
61         int N = in.readInt();
62         System.out.println(N);
63         Point[] points = new Point[N];
64         for (int i = 0; i < N; i++) {
65             int x = in.readInt();
66             int y = in.readInt();
67             System.out.println("x:" + x + " y:" + y);
68             points[i] = new Point(x, y);
69         }
70 
71         // draw the points
72         StdDraw.show(0);
73         StdDraw.setXscale(0, 32768);
74         StdDraw.setYscale(0, 32768);
75         for (Point p : points) {
76             p.draw();
77         }
78         StdDraw.show();
79 
80         // print and draw the line segments
81         BruteCollinearPoints collinear = new BruteCollinearPoints(points);
82         for (LineSegment segment : collinear.segments()) {
83             StdOut.println(segment);
84             segment.draw();
85         }
86     }
87 }
BruteCollinearPoints

由于这个方法时间复杂度太高,于是提出一种N2 log N 的方法,核心思路就是,通过实现一个排序方法,使点始终保持有序性来提高效率。看到N*NlogN,就想到了遍历点后排序,orz。下面是官方给出的算法描述:

  • Think of p as the origin.
  • For each other point q, determine the slope it makes with p.
  • Sort the points according to the slopes they makes with p.
  • Check if any 3 (or more) adjacent points in the sorted order have equal slopes with respect to p. If so, these points, together with p, are collinear.

实现的思路就是:

  1. 对所有点排序,越靠近左下角的点越小
  2. 遍历每一个点,遍历点P过程中,先将其他点根据与P的斜率进行排序
  3. 对排序后的其他点进行遍历,若有斜率相同超过4个以上的点,即是要找的线段

按照这个思路,很快就实现除了代码,需要注意的除了边界等细节问题,再有就是,如何避免子线段和重复线段。这两个问题把我卡住很久。

先说如何解决子线,所谓子线段就是原本线段:a->b->c->d->e是符合条件的,我将其取出,但是我又把b->c->d->e也当作新的线段取出来了。一开始的思路是,每次遍历一个点,都将其所有在一条线段上的点找出来,然后将这些点集sort一下,取最大最小。即在搜寻b的时候,将a,c,d,e和其本身b进行排序,取最小a和最大e。这样问题就解决了,但是同样还有一个问题是,由于每个点都进行了这样的操作,会导致有很多重复的线段。通过a得到线段(a,e),通过b又得到一边(a,e),所以结果是需要去重的。非常自然的想到了hashset,保证了时间复杂度不高。

然而使用hashcode的思路都无法实现...原因是poin和segment两个class都继承了Object的hashcode()方法,但是仅仅是在调用时抛出异常...至于为何会这样,原因是第三周还没有讲到hashtable这个数据结构orz

这条路不行,退一步,用toString()方法,将其toString后,存入hashset,来检查是否有重复的线段。我太机智了。不过这样依然是不是100%的通过,看了下testcase,竟然有一条fail是说我不应该依赖于toString()方法...

 

为什么不让?我陷入了沉思,comment中提到,这个方法仅供debug用,不要用于实现算法...不过细想其实是非常有道理的。我的算法本身实际上是依赖于comparable接口以及camparator的实现。而不是依赖于point和segment这两个具体的类。所以本身我就不应该去依赖于这两个类其他的方法,用了toString()只能说是非常tricky的方法。这样的做法,做到了解耦。看到这个testcase,感觉自己和名校的学生差距就是在这些细节上拉开的。不做这样的题,意识不到interface到底有啥用,不就是定义了几个未实现的方法吗?可是正是通过约定好的接口,做到了解耦。以后如果有人要修改point toString(),依然不会影响到我的程序的正确性。这就无形中提高了效率。

 

好了,回来说说我是怎么解决这个问题的。注意到,我在遍历之前,就先sort了点,这个sort有啥用呢?这个sort意味着,等会对于每个点找共线线段时,是按照从左下往右上的顺序来的,看图说话:

排过序的点是按照1,2,3,4,5的顺序来遍历的。当选取点1时,找到共线的5个点{1,2,3,4,5},因此我们选择最大最小作为线段(1,5)。

当选取点2时,找到了共线的5个点{1,2,3,4,5},这里也这里发现2<1,由于有序性,我们可以确认在此之前就有(1,5)被选取过了,所以这次的选择可以直接跳过。

这意味着,只要是当前遍历的点P不是共线点集中最小的点,我们都可以直接抛弃。这样就完美避免了重复的线段。

至此,根据这个方案,这个assignment圆满解决了。

一下是fast方法的实现,其他的代码就不贴了。

package collinear;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdOut;public class FastCollinearPoints {private int N;private ArrayList<LineSegment> segment = new ArrayList<LineSegment>();private Point[] points;public FastCollinearPoints(Point[] points) {if (points == null)throw new java.lang.NullPointerException();this.points = new Point[points.length];for (int i = 0; i < points.length; i++) {this.points[i] = points[i];}N = 0;Arrays.sort(this.points);// remove repeat pointsfor (int i = 0; i < this.points.length - 1; i++) {if (this.points[i].compareTo(this.points[i + 1]) == 0) {throw new java.lang.IllegalArgumentException();}}Point[] temp = new Point[this.points.length];Point[] copy = new Point[this.points.length];ArrayList<ArrayList<Point>> end_sets = new ArrayList<>();// use a copy to sortfor (int i = 0; i < this.points.length; i++) {temp[i] = copy[i] = this.points[i];end_sets.add(new ArrayList<Point>());}Arrays.sort(copy);// to sort by slopefor (int i = 0; i < copy.length - 1; i++) {Arrays.sort(temp, copy[i].slopeOrder());// find same slope copy then save them as segment in segment// ArrayListint count = 1;double slope0 = copy[i].slopeTo(temp[0]);ArrayList<Point> set = new ArrayList<>();for (int j = 0; j < temp.length; j++) {// record max pointdouble slope1 = copy[i].slopeTo(temp[j]);if (slope1 == slope0) {set.add(temp[j]);count++;if (count > 2 && j == temp.length - 1) {set.add(copy[i]);Collections.sort(set);// System.out.println(set);if (set.get(0).compareTo(copy[i]) < 0) {} else {// no key, no setsegment.add(new LineSegment(set.get(0), set.get(set.size() - 1)));N++;}                        count = 1;}} else {if (count > 2) {set.add(copy[i]);Collections.sort(set);// System.out.println(set);if (set.get(0).compareTo(copy[i]) < 0) {} else {// no key, no setsegment.add(new LineSegment(set.get(0), set.get(set.size() - 1)));N++;}}set = new ArrayList<>();set.add(temp[j]);count = 1;}slope0 = slope1;}}}// the number of line segmentspublic int numberOfSegments() {return N;}// the line segmentspublic LineSegment[] segments() {LineSegment[] results = new LineSegment[N];for (int i = 0; i < N; i++) {results[i] = segment.get(i);}return results;}public static void main(String[] args) {// read the N points from a file// In in = new In(args[0]);In in = new In("./collinear/rs1423.txt");int N = in.readInt();// System.out.println(N);Point[] points = new Point[N];for (int i = 0; i < N; i++) {int x = in.readInt();int y = in.readInt();// System.out.println("x:" + x + " y:" + y);points[i] = new Point(x, y);}// draw the pointsStdDraw.show(0);StdDraw.setXscale(0, 32768);StdDraw.setYscale(0, 32768);for (Point p : points) {p.draw();}StdDraw.show();// // print and draw the line segmentsFastCollinearPoints collinear = new FastCollinearPoints(points);for (LineSegment segment : collinear.segments()) {StdOut.println(segment);segment.draw();}}
}
FastCollinearPoints

老规矩,亮下分数:

 

 

转载于:https://www.cnblogs.com/yilujuechen/articles/4856540.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397434.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java BigDecimal详解

1.引言 float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算&#xff0c;这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而&#xff0c;它们没有提供完全精确的结果&#xff0c;所以不应该被用于要求精确结果的场合。但是…

windows 串口编程 c语言,windows下C语言版串口发送程序(基于VS2017)

#include "tchar.h"#include int main(){/*****************************打开串口*************************************/HANDLE hCom;//全局变量&#xff0c;串口句柄hCom CreateFile(_T("COM3"),//COM3口GENERIC_READ | GENERIC_WRITE,//允许读和写0,/…

scikit-learn决策树算法类库使用小结

之前对决策树的算法原理做了总结&#xff0c;包括决策树算法原理(上)和决策树算法原理(下)。今天就从实践的角度来介绍决策树算法&#xff0c;主要是讲解使用scikit-learn来跑决策树算法&#xff0c;结果的可视化以及一些参数调参的关键点。 1. scikit-learn决策树算法类库介绍…

c语言编写程序求8,使用c语言编写程式,实现计算1*2*3+4*5*6+7*8*9+……+28*29*30的值...

使用c语言编写程式&#xff0c;实现计算1*2*34*5*67*8*9……28*29*30的值以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;使用c语言编写程式&#xff0c;实现计算1*2*34*5*67*8*9……28*29*3…

PHP 正则表达式分割 preg_split 与 split 函数

为什么80%的码农都做不了架构师&#xff1f;>>> preg_split() preg_ split() 函数用于正则表达式分割字符串。 语法&#xff1a; array preg_split( string pattern, string subject [, int limit [, int flags]] ) 返回一个数组&#xff0c;包含 subject 中沿着与…

简单学C——第五天

结构体 首先明确&#xff0c;结构体是一种构造的数据类型&#xff0c;是一种由多个数据类型如 int&#xff0c;char&#xff0c;double&#xff0c;数组或者结构体......组成的类型,现在告诉大家如何定义一个结构体。在定义int整型变量时&#xff0c;大家肯定都知道 int a; 即…

Codeforces Round #325 (Div. 2) B. Laurenty and Shop 前缀和

B. Laurenty and Shop Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/586/problem/BDescription A little boy Laurenty has been playing his favourite game Nota for quite a while and is now very hungry. The boy wants to make sau…

android gallery自动播放,可循环显示图像的Android Gallery组件

类型&#xff1a;源码相关大小&#xff1a;23.6M语言&#xff1a;中文 评分&#xff1a;9.1标签&#xff1a;立即下载第 4 页 实现循环显示图像的Gallery组件实现循环显示图像的Gallery组件在本节将组出与循环显示图像相关的ImageAdapter类的完整代码。读者可以从中看到上一节介…

curl网站开发指南

curl网站开发指南 作者&#xff1a; 阮一峰 日期&#xff1a; 2011年9月 4日 我一向以为&#xff0c;curl只是一个编程用的函数库。 最近才发现&#xff0c;这个命令本身&#xff0c;就是一个无比有用的网站开发工具&#xff0c;请看我整理的它的用法。 curl网站开发指南 阮一…

Who Gets the Most Candies? POJ - 2886 (线段树)

按顺时针给出n个小孩&#xff0c;n个小孩每个人都有一个纸&#xff0c;然后每个人都有一个val&#xff0c;这个val等于自己的因子数&#xff0c;如果这个val是正的&#xff0c;那就顺时针的第val个孩子出去&#xff0c;如果是负的话&#xff0c;就逆时针的第val个孩子出去&…

javax.validation.ValidationException: Unable to find a default provider

2019独角兽企业重金招聘Python工程师标准>>> [ERROR] [2016-11-16 13:58:21 602] [main] (FrameworkServlet.java:457) Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name org.springframewo…

android视频播放器api,03.视频播放器Api说明

03.视频播放器Api说明目录介绍01.最简单的播放02.如何切换视频内核03.切换视频模式04.切换视频清晰度05.视频播放监听06.列表中播放处理07.悬浮窗口播放08.其他重要功能Api09.播放多个视频10.VideoPlayer相关Api11.Controller相关Api12.边播放边缓存api13.类似抖音视频预加载14…

Taurus.MVC 2.0 开源发布:WebAPI开发教程

背景&#xff1a; 有用户反映&#xff0c;Tausus.MVC 能写WebAPI么&#xff1f; 能&#xff01; 教程呢&#xff1f; 嗯&#xff0c;木有&#xff01; 好吧&#xff0c;刚好2.0出来&#xff0c;就带上WEBAPI教程了&#xff01; 开源地址&#xff1a; https://github.com/cyq116…

chrome瀏覽器去掉緩存的方法

方法一&#xff1a; 1.開發說打開開發者工具 勾選這個訪問可以 方法二: commandshiftR 转载于:https://www.cnblogs.com/kaibindirver/p/9378572.html

sony z2 android 5.0,索尼Xperia Z2 5.0 root教程_索尼Z2获取5.0系统的root

来说一下咱们的索尼Xperia Z2手机的5.0系统的root&#xff0c;因为现在很多机友的系统是5.0的&#xff0c;可是对于5.0的系统很多机友还不知道如何进行root操作&#xff0c;之前的针对4.4的系统的root方法肯定是用不到5.0的系统上的&#xff0c;因此需要专门的针对5.0的root软件…

ABP文档 - Javascript Api - AJAX

本节内容&#xff1a; AJAX操作相关问题ABP的方式 AJAX 返回信息处理错误 HTTP 状态码WrapResult和DontWrapResult特性 Asp.net Mvc 控制器Asp.net Web Api 控制器动态Web Api层Asp.net Core 控制器动态Web Api层AJAX操作相关问题 执行一个AJAX调用在现在的应用里非常常见&…

stack overflow--技术问答网站

转自&#xff1a;http://baike.baidu.com/link?urleMR6Pwdk9IkauI5B3nZb2Yo3VUAcK6vQfrMpcSMPWqgH0ngqFkup3Gdr3t_s_yZe_UFwkR8c1pboaxhEuY-iwF_nGiUYHajEPMO6Y1kqWvT8aPz7a_T6t3a1vxyTccgKl_UIx1cU-6IP7qjre2ijtq Stack Overflow是一个与程序相关的IT技术问答网站。用户可以在…

uvalive 4973 Ardenia

题意&#xff1a;给出空间两条线段&#xff0c;求距离。 注意输出格式&#xff01; 1 #include<cstdio>2 #include<cmath>3 #include<algorithm>4 using namespace std;5 6 struct Point37 {8 int x, y, z;9 Point3(int x0, int y0, int z0):x(x),y(…

rz和sz上传下载文件

安装软件包 yum install lrzsz 上传文件&#xff0c;输入rz选择文件上传(可以按住shift键多选) # rz sz 下载文件到本地&#xff0c;选择保存文件夹 # sz dd xshell设置默认上传下载文件夹 转载于:https://www.cnblogs.com/fcing/p/9382377.html

怎样在html中设置首字母大写,javascript如何设置字符串首字母大写?

给出一个字符串&#xff0c;如何确保字符串的首字母都大写&#xff1f;下面本篇文章就来给大家介绍一下使用javascript设置首字母大写的方法&#xff0c;希望对大家有所帮助。在javascript中&#xff0c;可以使用slice()方法、toUpperCase()方法和toLowerCase()方法来设置首字母…