java——前三次作业博客浅谈
一、 前言
纵观这三次java作业,难度毋庸置疑是逐层递进的,在单次作业中尤为明显。虽说第一次作业是在我基础知识不扎实,对面向对象这一概念还没有具体认识的时候进行的,难度对于那时的我来说有些超出能力范围。但就算放到现在,以我现在的知识重新写一次,我也未必能全盘拿下,题目集的质量可见一斑。就拿我印象中最深的第三次作业来说,知识点涵盖面广,题目复杂度高,单一题可能要花上一天时间去修改完善代码,又因为自己本身没有安排好时间去处理题目,导致匆忙完成的代码质量不高且没有形成体系,可能这也从侧面增加了题目的难度。总的来说,题目是老师精心挑选出来的,题量以及难易度肯定是没得说,都在可接受范围内,只要我们认真结合老师点拨与网络资源,这几次作业可以成为我们学习路上典例中的典例,值得不断探索学习。
二、 设计与分析
(1) 题目集2的7-2是作业中遇到的第一个相较其他旧题有较大区别的难题,它难在对于用户的各种异常输入进行判断的同时筛选出正确格式并提取出有效数据以进行计算。
例如
在此输入样例中,程序需要对输入的一长串数据进行分析,按要求分别提取出相应三个有效数据,再进行内容判断与输出。难点首先在于如何判断这是否为可提取数据输入,其次如何将其中无用数字过滤并分割开有效数字分别储存在某变量中,最后对变量中数据进行相应判断,做出相应正确格式输出。
我的做法是在循环中判断数据是否足够长并记录首个非1数字,再将首个非1数字后11位数据保存在字符串中,即每次循环时都将有效数据先“拿出来”进行操作之后再进行下一轮循环读取数据。这样的好处在于相较直接“拿出”所有有效数据操作更加简洁明了,省去了分别讨论每个有效数据的重复步骤,统一化讨论数据格式输出,易于修改优化。
可以看到通过语句str.charAt(i)来操作字符串中保存的数字,对不同情况进行讨论后输出题目相应要求输出语句。
此类题型在于考察对字符串的操作,能否正确使用charAt、substring等字符串操作语句对数据进行判断分割以达到预期效果。有一说一,substring是真的好用,不同控制方法能有效分割字符串,强大方便,解决了很多使用如将数据存入数组再操作等方法会出现的莫名其妙的bug,值得牢记以及灵活运用。
(2) 题目集3的第一题为计算两点间距离。输入两点坐标后计算距离,看起来比较简单的问题,但仍有难点。
例如
如输入样例二,坐标数字前只能有一个正负号或者没有,以及样例三,只能有两个点坐标。这就要求程序能对输入数据进行格式判断。而运用正则表达式可很好解决这个问题。
1 static int check(String str) { 2 if(str.matches("(([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){2}.*")==true) 3 return 0; 4 if(str.matches("([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)) ([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))")==true) 5 return 1; 6 else 7 return 2; 8 }
调用正则表达式str.matches("(([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){2}.*")可对多于两个点的输入进行筛选,而唯一正确的输入则因为坐标间空格分隔符的存在而单独写了一条正则表达式str.matches("([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)) ([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))")来匹配。获取输入后调用拆分语句String[] tokens = str.split("[, ]");当识别到空格符或逗号时将数据分割。随后进行坐标距离的计算。
正则表达式是非常实用且常用的格式判断方法,灵活运用可以减少很多不必要的格式讨论。
(3) 题目集3第二题为线的有关计算。为第一题的升级版,由点上升到线。其中对用户输入的不同也有不同的计算要求。但首先,肯定是对输入格式的判断以保证输入数据的合法性,此处又对正则表达式进行灵活运用,用户选择的计算不同,输入的点数量也不同,需要判断的格式也不同,所以使用stwich语句进行不同选择的格式判断。判断完成后同样使用String[] tokens = str.split("[, ]");语句分割识别到的有效输入数据,储存在tokens数组中。同时在每个计算方法开始前进行输入点坐标是否重复的判断。
1 static int check(String str) { 2 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){3}([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 3 return 0; 4 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){2}([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 5 return 1; 6 if(str.matches("[1-5]:(([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)) ([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 7 return 2; 8 if(str.matches("[1-5]:(([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 9 return 3; 10 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){4}(([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))*)")==true) 11 return 3; 12 else 13 return 4; 14 }
如果用户选择计算1,计算点构成的线斜率,若斜率不存在则输出"Slope does not exist"。通过其他函数判断完格式正确后算法较为简单。
1 if(Check.b==Check.d&&Check.c==Check.e) { 2 System.out.println("points coincide"); 3 return; 4 } 5 double x=0; 6 if(Check.b==Check.d&&Check.c!=Check.e) { 7 System.out.println("Slope does not exist"); 8 } 9 else { 10 x=(Check.c-Check.e)/((Check.b-Check.d)); 11 System.out.println(x); 12 }
如果用户选择计算2,输入三个点坐标,输出第一个点与另外两点连线的垂直距离。此处语法上无问题,只要数学知识储备足够即可,此处未考虑误差值,因为误差值极小时可视作0。
1 if((Check.b==Check.d&&Check.c==Check.e)||(Check.b==Check.f&&Check.c==Check.g)||(Check.d==Check.f&&Check.e==Check.g)) { 2 System.out.println("points coincide"); 3 return; 4 } 5 System.out.println(Math.abs(((Check.e-Check.g)*Check.b+(Check.f-Check.d)*Check.c+Check.d*Check.g-Check.e*Check.f)/(Math.sqrt(Math.pow(Check.e-Check.g, 2) +Math.pow(Check.d-Check.f, 2))))); 6 }
如果用户选择计算3,输入三个点坐标,判断三个点是否在一条线上,输出true或者false。此处与计算2一样,无语法问题,纯数学知识。
1 if((Check.b==Check.d&&Check.c==Check.e)||(Check.b==Check.f&&Check.c==Check.g)||(Check.d==Check.f&&Check.e==Check.g)) { 2 System.out.println("points coincide"); 3 return; 4 } 5 6 if(Math.abs((Check.g-Check.c)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.f-Check.b))==0) { 7 System.out.println("true"); 8 } 9 else 10 System.out.println("false"); 11 }
如果用户选择计算4,输入四个点坐标,判断前两个点所构成的直线与后两点构成的直线是否平行,输出true或者false。此处因为有四个点输入,所以四个点两两判断是否重复。基本算法是判断前两个点构成的直线斜率与后两个点构成的直线斜率是否相等,当斜率都不存在时判断横坐标是否相等即可。
1 if((Check.b==Check.d&&Check.c==Check.e)||(Check.b==Check.f&&Check.c==Check.g)||(Check.d==Check.f&&Check.e==Check.g)||(Check.b==Check.h&&Check.c==Check.i)||(Check.d==Check.h&&Check.e==Check.i)||(Check.f==Check.h&&Check.g==Check.i)) { 2 System.out.println("points coincide"); 3 return; 4 } 5 double x,y; 6 x=y=0; 7 x=(Check.c-Check.e)/(Check.b-Check.d); 8 y=(Check.g-Check.i)/(Check.f-Check.h); 9 if((x==y)||((Check.c==Check.e)&&(Check.g==Check.i))) { 10 System.out.println("true"); 11 } 12 else 13 System.out.println("false"); 14 }
如果用户选择计算5,输入四个点坐标,计算输出前两个点所构成的直线与后两点构成的直线的交点坐标,x、y坐标之间以英文分隔",",并输出交叉点是否在两条线段之内(不含四个端点)的判断结果(true/false),判断结果与坐标之间以一个英文空格分隔。若两条线平行,没有交叉点,则输出"is parallel lines,have no intersection point"。首先运用计算4中的方法判断两直线是否平行,再计算两直线相交的交点坐标,再通过坐标值大小来判断交点是否位于线段内。
1 if((Check.b==Check.d&&Check.c==Check.e)||(Check.b==Check.f&&Check.c==Check.g)||(Check.d==Check.f&&Check.e==Check.g)||(Check.b==Check.h&&Check.c==Check.i)||(Check.d==Check.h&&Check.e==Check.i)||(Check.f==Check.h&&Check.g==Check.i)) { 2 System.out.println("points coincide"); 3 return; 4 } 5 double x,y; 6 x=y=0; 7 x=(Check.c-Check.e)/(Check.b-Check.d); 8 y=(Check.g-Check.i)/(Check.f-Check.h); 9 if((x==y)||((Check.b==Check.d&&Check.c!=Check.e)&&(Check.f==Check.h&&Check.g!=Check.i))) { 10 System.out.println("is parallel lines,have no intersection point"); 11 } 12 else 13 { 14 double q,w; 15 boolean flag; 16 q=w=0; 17 q=(Check.f*(Check.g-Check.i)*(Check.d-Check.b)-Check.b*(Check.e-Check.c)*(Check.f-Check.h)+(Check.c-Check.g)*(Check.d-Check.b)*(Check.f-Check.h))/((Check.g-Check.i)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.f-Check.h)); 18 w=(Check.c*(Check.d-Check.b)*(Check.g-Check.i)-Check.g*(Check.f-Check.h)*(Check.e-Check.c)+(Check.f-Check.b)*(Check.g-Check.i)*(Check.e-Check.c))/((Check.d-Check.b)*(Check.g-Check.i)-(Check.f-Check.h)*(Check.e-Check.c)); 19 if((q>Check.b&&q<Check.d)||(q>Check.d&&q<Check.b)) 20 { 21 if((w>Check.c&&w<Check.e)||(w>Check.e&&w<Check.c)) 22 { 23 flag=true; 24 } 25 else 26 flag=false; 27 28 } 29 else if((q>Check.f&&q<Check.h)||(q>Check.h&&q<Check.f)) 30 { 31 if((w>Check.g&&w<Check.i)||(w>Check.i&&w<Check.g)) 32 { 33 flag=true; 34 } 35 else 36 flag=false; 37 } 38 else 39 flag=false; 40 System.out.println(q + "," + w + " " + flag); 41 } 42 43 }
(4)题目3第三题为三角形的计算,又在第二题的基础上增加了难度,进行由线构成的三角形的有关计算。同时输入格式在前两题的输入格式的基础上又增加了判断是否能构成三角形的算法,但实质上可拆分为判断两点是否重合与三点是否一线。此题还有对输出数据保留小数点的要求,输出的数据若小数点后超过6位,只保留小数点后6位,多余部分采用四舍五入规则进到最低位。小数点后若不足6位,按原始位数显示,不必补齐。例如:1/3的结果按格式输出为 0.333333,1.0按格式输出为1.0。可通过DecimalFormat df = new DecimalFormat("0.0#####");语句来控制小数点位数问题。本题有输入五个点的选择,所以在第二题的基础上增加一次正确输入格式的判断。
1 static int check(String str) { 2 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){4}([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 3 return 0; 4 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){3}([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 5 return 1; 6 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){2}([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 7 return 2; 8 if(str.matches("[1-5]:(([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))(\\s([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))?)")==true) 9 return 3; 10 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){5}(([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))*)")==true) 11 return 3; 12 else 13 return 4; 14 }
如果用户选择计算1,输入三个点坐标,判断是否是等腰三角形、等边三角形,判断结果输出true/false,两个结果之间以一个英文空格符分隔。计算每两点间距离后判断三角形三边是否相等即可。
1 if(((Check.b==Check.d&&Check.c==Check.e)||(Check.b==Check.f&&Check.c==Check.g)||(Check.d==Check.f&&Check.e==Check.g))||Math.abs((Check.g-Check.c)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.f-Check.b))==0) { 2 System.out.println("data error"); 3 return; 4 } 5 double x,y,z; 6 x=y=z=0; 7 x=Math.sqrt((Check.b-Check.d)*(Check.b-Check.d)+((Check.c-Check.e)*(Check.c-Check.e))); 8 y=Math.sqrt((Check.b-Check.f)*(Check.b-Check.f)+((Check.c-Check.g)*(Check.c-Check.g))); 9 z=Math.sqrt((Check.d-Check.f)*(Check.d-Check.f)+((Check.e-Check.g)*(Check.e-Check.g))); 10 if(x==y||x==z||y==z) 11 System.out.print("true"); 12 else 13 System.out.print("false"); 14 if(x==y&&x==z) 15 System.out.print(" true"); 16 else 17 System.out.print(" false"); 18
如果用户选择计算2,输入三个点坐标,输出周长、面积、重心坐标,三个参数之间以一个英文空格分隔,坐标之间以英文","分隔。计算每两点间距离后相加即得到三角形周长,用海伦公式 s =(x+y+z)/2; S=(double) Math.sqrt(s*(s-x)*(s-y)*(s-z));可利用三边长计算出三角形面积,三角形重心坐标公式为三点对应坐标相加/3。
1 if(((Check.b==Check.d&&Check.c==Check.e)||(Check.b==Check.f&&Check.c==Check.g)||(Check.d==Check.f&&Check.e==Check.g))||Math.abs((Check.g-Check.c)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.f-Check.b))==0) { 2 System.out.println("data error"); 3 return; 4 } 5 double x,y,z; 6 7 x=y=z=0; 8 x=Math.sqrt((Check.b-Check.d)*(Check.b-Check.d)+((Check.c-Check.e)*(Check.c-Check.e))); 9 y=Math.sqrt((Check.b-Check.f)*(Check.b-Check.f)+((Check.c-Check.g)*(Check.c-Check.g))); 10 z=Math.sqrt((Check.d-Check.f)*(Check.d-Check.f)+((Check.e-Check.g)*(Check.e-Check.g))); 11 DecimalFormat df = new DecimalFormat("0.0#####"); 12 System.out.print(df.format(x+y+z)); 13 double s =(x+y+z)/2; 14 double S = (double) Math.sqrt(s*(s-x)*(s-y)*(s-z)); 15 System.out.print(" " + df.format(S) + " "); 16 System.out.print(df.format((Check.b+Check.d+Check.f)/3) + "," + df.format((Check.c+Check.e+Check.g)/3));
如果用户选择计算3,输入三个点坐标,输出是钝角、直角还是锐角三角形,依次输出三个判断结果(true/false),以一个英文空格分隔。首先通过计算每两点间距离得出三边长,再利用三边长得出各角cos的值,有一个cos值小于0即钝角三角形,有一个cos值等于0即直角三角形,三个cos值均大于0即锐角三角形。
1 double x,y,z,q,w,e; 2 x=y=z=0; 3 if(((Check.b==Check.d&&Check.c==Check.e)||(Check.b==Check.f&&Check.c==Check.g)||(Check.d==Check.f&&Check.e==Check.g))||Math.abs((Check.g-Check.c)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.f-Check.b))==0) { 4 System.out.println("data error"); 5 return; 6 } 7 8 x=y=z=q=w=e=0; 9 x=Math.sqrt((Check.b-Check.d)*(Check.b-Check.d)+((Check.c-Check.e)*(Check.c-Check.e))); 10 y=Math.sqrt((Check.b-Check.f)*(Check.b-Check.f)+((Check.c-Check.g)*(Check.c-Check.g))); 11 z=Math.sqrt((Check.d-Check.f)*(Check.d-Check.f)+((Check.e-Check.g)*(Check.e-Check.g))); 12 DecimalFormat df = new DecimalFormat("0.0#####"); 13 q=(y*y+z*z-x*x)/(2*y*z); 14 w=(x*x+z*z-y*y)/(2*x*z); 15 e=(y*y+x*x-z*z)/(2*y*x); 16 if(q<0||w<0||e<0) { 17 System.out.print("true "); 18 } 19 else 20 System.out.print("false "); 21 if(q==0||w==0||e==0) 22 System.out.print("true "); 23 else 24 System.out.print("false "); 25 if(q>0&&w>0&&e>0) 26 System.out.print("true"); 27 else 28 System.out.print("false");
如果用户输入计算4,输入五个点坐标,输出前两个点所在的直线与三个点所构成的三角形相交的交点数量,如果交点有两个,则按面积大小依次输出三角形被直线分割成两部分的面积。若直线与三角形一条线重合,输出"The point is on the edge of the triangle"。首先判断直线与三角形是否重合,只要判断直线与三角形某一边斜率相等时构成直线的两点与三角形某边上一端点三点是否共线即可。面积计算算法正在研究中。
1 if(Check.b==Check.d&&Check.c==Check.e) { 2 System.out.println("points coincide"); 3 return; 4 } 5 if(((Check.f==Check.h&&Check.g==Check.i)||(Check.f==Check.j&&Check.g==Check.k)||(Check.h==Check.j&&Check.i==Check.k))||Math.abs((Check.k-Check.g)*(Check.h-Check.f)-(Check.i-Check.g)*(Check.j-Check.f))==0) { 6 System.out.println("data error"); 7 return; 8 } 9 if(Judge.judge2(Check.b,Check.c, Check.d, Check.e, Check.f, Check.g, Check.h, Check.i)==1) { 10 if(Math.abs((Check.g-Check.c)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.f-Check.b))==0) 11 System.out.println("The point is on the edge of the triangle"); 12 a++; 13 } 14 15 if(Judge.judge2(Check.b,Check.c, Check.d, Check.e, Check.f, Check.g, Check.j, Check.k)==1) { 16 if(Math.abs((Check.g-Check.c)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.f-Check.b))==0) 17 System.out.println("The point is on the edge of the triangle"); 18 a++; 19 } 20 if(Judge.judge2(Check.b,Check.c, Check.d, Check.e, Check.h, Check.i, Check.j, Check.k)==1) { 21 if(Math.abs((Check.i-Check.c)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.h-Check.b))==0) 22 System.out.println("The point is on the edge of the triangle"); 23 a++; 24 }
如果用户选择计算5,输入四个点坐标,输出第一个是否在后三个点所构成的三角形的内部(输出in the triangle/outof triangle)。
必须使用射线法,原理:由第一个点往任一方向做一射线,射线与三角形的边的交点(不含点本身)数量如果为1,则在三角形内部。如果交点有两个或0个,则在三角形之外。若点在三角形的某条边上,输出"on the triangle"。由于不懂如何创造射线以及受计算交点是否在线段内的算法启发,模拟创造射线的算法解决问题。首先判断点是否在三角形上只要判断三点是否一线即可,再找出三角形端点中横、纵坐标的最大者,随机生成比坐标最大值稍大的随机数坐标以保证随机数坐标始终在三角形外且距某端点较近。接下来利用题目3第二题中直线交点是否在线段内算法判断第一个点与随机数构成的直线与三角形各边的交点是否在线段内。若点在三角形内则在线段内交点数量有且只有一个,若点在三角形外则可能有两个及以上。
1 if(((Check.d==Check.f&&Check.e==Check.g)||(Check.d==Check.h&&Check.e==Check.i)||(Check.f==Check.h&&Check.g==Check.i))||Math.abs((Check.i-Check.e)*(Check.f-Check.d)-(Check.g-Check.e)*(Check.h-Check.d))==0) { 2 System.out.println("data error"); 3 return; 4 } 5 if(Math.abs((Check.g-Check.c)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.f-Check.b))==0) { 6 System.out.println("on the triangle"); 7 return; 8 } 9 if(Math.abs((Check.i-Check.c)*(Check.d-Check.b)-(Check.e-Check.c)*(Check.h-Check.b))==0) { 10 System.out.println("on the triangle"); 11 return; 12 } 13 if(Math.abs((Check.i-Check.c)*(Check.f-Check.b)-(Check.g-Check.c)*(Check.h-Check.b))==0) { 14 System.out.println("on the triangle"); 15 return; 16 } 17 double max,max2,q,w; 18 boolean falg=false; 19 int t=0; 20 max=max2=q=w=0; 21 Random rand = new Random(); 22 max=(Check.d>Check.f? Check.d:Check.f)>Check.h? (Check.d>Check.f? Check.d:Check.f):Check.h; 23 max2=(Check.e<Check.g? Check.e:Check.g)<Check.i? (Check.e<Check.g? Check.e:Check.g):Check.i; 24 double randNumber1 =rand.nextInt(99) + max; 25 double randNumber2 =rand.nextInt(99) + max2; 26 if(Judge.judge3(Check.b, Check.c, randNumber1, randNumber2, Check.d, Check.e, Check.f, Check.g)==1) { 27 t++; 28 } 29 if(Judge.judge3(Check.b, Check.c, randNumber1, randNumber2, Check.d, Check.e, Check.h, Check.i)==1) { 30 t++; 31 } 32 if(Judge.judge3(Check.b, Check.c, randNumber1, randNumber2, Check.f, Check.g, Check.h, Check.i)==1) { 33 t++; 34 } 35 if(t==1) { 36 System.out.println("in the triangle"); 37 } 38 else 39 System.out.println("outof triangle");
三、踩坑心得
对于这几次作业,遇到了非常多问题,大多是因为自身基础知识不扎实而导致的。常见的有因为空格符存在而无法正确判断输入格式错误,判断输出格式层次错误,不能很好的引用其他类,代码泛用性不佳等。以综合度最高的题目3第三题为例,能够发现许多容易犯错的地方。
首先是输入格式问题。相信这也是很多同学最难开始也是最容易出错的地方,即使能够运用正则表达式进行格式判断,但是如何正确的书写相应的正则表达式依旧是一大问题。例如
1 static int check(String str) { 2 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){4}([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 3 return 0; 4 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){3}([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 5 return 1; 6 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){2}([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))")==true) 7 return 2; 8 if(str.matches("[1-5]:(([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))(\\s([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))?)")==true) 9 return 3; 10 if(str.matches("[1-5]:((([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*))\\s){5}(([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)),([+-]?([1-9]\\d*\\.\\d*|0\\.\\d*|0|[1-9]\\d*)))*)")==true) 11 return 3; 12 else 13 return 4; 14 }
不仅要正确书写浮点数的匹配表达式,更要考虑符号问题,其中正负号只能出现一次或零次,空格符与逗号的存在也是让难度上升了一个档次。因为数据末尾无空格而数据分割有空格,在点较少时无法直接套用一个固定模式来匹配输入是否正确,只能将其全部写出。当输入点较多时,可套用一套固定模式的表达式进行判断,最后再书写特殊结尾即可。因此格式判断我做成了多层 if 语句来判断究竟是哪一种输入格式。以后在遇到类似情况时也可借鉴或根据情况自行灵活调整表达式。
其次是对输入数据的处理问题。在进行完数据的格式判断后,需要使用String[] tokens = str.split("[:, ]");拆分语句对其进行拆分,读取到空格或逗号就分割数据并存储到tokens数组中。尤其要注意空格与逗号外需要一个中括号括起,否则读取数据将错误(隐形错误,储存在数组中数据不是期望值)。因为输入点数量不同,提取分割出来的数据数量也不同,所以储存时为了不使数组越界我使用多个 if 对不同情况分别进行储存数据的操作。java中应该存在不会使数组越界就能重复改变储存大小的数组的方法,但我学识浅薄就没有使用,应在以后的学习中继续探索。
if(check(str)==0) { String[] tokens = str.split("[:, ]"); a = Integer.parseInt(tokens[0]); b = Double.parseDouble(tokens[1]); c = Double.parseDouble(tokens[2]); d = Double.parseDouble(tokens[3]); e = Double.parseDouble(tokens[4]); f = Double.parseDouble(tokens[5]); g = Double.parseDouble(tokens[6]); h = Double.parseDouble(tokens[7]); i = Double.parseDouble(tokens[8]); j = Double.parseDouble(tokens[9]); k = Double.parseDouble(tokens[10]); if(a==4) return true; else { System.out.println("wrong number of points"); return false; } } else if(check(str)==1) { String[] tokens = str.split("[:, ]"); a = Integer.parseInt(tokens[0]); b = Double.parseDouble(tokens[1]); c = Double.parseDouble(tokens[2]); d = Double.parseDouble(tokens[3]); e = Double.parseDouble(tokens[4]); f = Double.parseDouble(tokens[5]); g = Double.parseDouble(tokens[6]); h = Double.parseDouble(tokens[7]); i = Double.parseDouble(tokens[8]); if(a==5) return true; else { System.out.println("wrong number of points"); return false; } } else if(check(str)==2) { String[] tokens = str.split("[:, ]"); a = Integer.parseInt(tokens[0]); b = Double.parseDouble(tokens[1]); c = Double.parseDouble(tokens[2]); d = Double.parseDouble(tokens[3]); e = Double.parseDouble(tokens[4]); f = Double.parseDouble(tokens[5]); g = Double.parseDouble(tokens[6]); if(a==1||a==2||a==3) return true; else { System.out.println("wrong number of points"); return false; } } else if(check(str)==3) System.out.println("wrong number of points"); else System.out.println("Wrong Format"); return false;
最后是算法优化问题。在进行各计算方法时,经常会出现测试点有些能通过有些不能通过的情况,在保证数据读取没问题后可考虑是否为算法的问题。例如题目3中第二题中的计算点到直线距离,用普通数学解方程得到的距离表达式尽管没错,但是由于现实中不可避免的误差带来的影响,需在表达式中引入1e6概念。这样的表达式才能通过测试点。
1 if((Check.b==Check.d&&Check.c==Check.e)||(Check.b==Check.f&&Check.c==Check.g)||(Check.d==Check.f&&Check.e==Check.g)) { 2 System.out.println("points coincide"); 3 return; 4 } 5 System.out.println(Math.abs(((Check.e-Check.g)*Check.b+(Check.f-Check.d)*Check.c+Check.d*Check.g-Check.e*Check.f)/(Math.sqrt(Math.pow(Check.e-Check.g, 2) +Math.pow(Check.d-Check.f, 2))))); 6 }
(4)改进建议
应在程序中更加凸显类与对象这一概念,有许多可由之前写过的代码衍生而来的算法都未很好的利用起来,没有明确的类的定义,导致后面要用之前的算法时只能复制黏贴这样的笨办法,应多学习如何通过类之间的关系做到灵活调用方法,为代码的可移植性打好基础。实际上题目的层次关系十分明显,就是为了引导我们能够实现方法的调用,以后做题学习时应多注意此类关系紧密的类型,尽力完善方法间的套用关系。完善类的同时,算法的优化也十分重要,算法优化不仅可以节省时间提高效率,还可以使程序更加简洁美观。此外可以在例如判断多个点输入时的正则表达式末尾不用 .* (可匹配任意字符,即超出正确点数量后输入任意字符均判定为输入点输入错误,但实际上有可能是格式错误的非法输入)语句一笔带过的概括式表达进行优化,避免一些不必要的bug。
(5)总结
通过这三次作业的学习,我初步认识到了java面向对象的编码理念,掌握了一些基本的类的操作,通过对象来管理属性,对数据格式的基础判断,java一些快捷语句的使用等等。同时也发现了自身许多问题,类的具象化还是不够完整清楚,不能让人直观明了,导致代码繁琐,复杂度高,阅读起来很困难,这都是需要改进的地方。老师课程与慕课等平台课程结合学习可以使学习效率最大化,老师也可以在课堂上与慕课内容互动联系,使学生更加印象深刻。作业的质量很高,难度适中且具一定挑战性,希望老师能继续出类似的高质量题目,为我们学习成长夯实基础,铸就典例。