面向对象程序设计PTA01总结
前言
几次PTA的作业着重练习类的构造方法、方法的调用、参数传递、对象的构造与使用;练习循环结构、控制结构;练习数据的输入与输出;学习编写结构清晰、逻辑正确、功能完善的java代码。学习使用工具对代码进行跟踪调试。理解抽象类与子类的关系,实现继承操作,构造类族;正确使用抽象类和子类实现多态操作;理解接口的意义,并实现行为的多态;理解了解抽象类、类以及接口三者的异同以及使用场合。掌握UML基本用法和画法,并能根据题目描述画出类、接口、类。整体题量适中,大部分的题目的难度适中,极个别题目有着鲜明的特点、特征。
设计与分析
本模块重点对题目提交的源码进行分析
题目集1的7-8
输入三角形三条边,判断该三角形为什么类型的三角形。
输入格式:
在一行中输入三角形的三条边的值(实型数),可以用一个或多个空格或回车分隔,其中三条边的取值范围均为[1,200]。
输出格式:
(1)如果输入数据非法,则输出“Wrong Format”; (2)如果输入数据合法,但三条边不能构成三角形,则输出“Not a triangle”; (3)如果输入数据合法且能够成等边三角形,则输出“Equilateral triangle”; (3)如果输入数据合法且能够成等腰直角三角形,则输出“Isosceles right-angled triangle”; (5)如果输入数据合法且能够成等腰三角形,则输出“Isosceles triangle”; (6)如果输入数据合法且能够成直角三角形,则输出“Right-angled triangle”; (7)如果输入数据合法且能够成一般三角形,则输出“General triangle”。
输入样例1:
在这里给出一组输入。例如:
50 50 50.0
输出样例1:
在这里给出相应的输出。例如:
Equilateral triangle
输入样例2:
在这里给出一组输入。例如:
60.2 60.2 80.56
输出样例2:
在这里给出相应的输出。例如:
Isosceles triangle
输入样例3:
在这里给出一组输入。例如:
0.5 20.5 80
输出样例3:
在这里给出相应的输出。例如:
Wrong Format
源代码:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in=new Scanner(System.in); double a=in.nextDouble(); double b=in.nextDouble(); double c=in.nextDouble(); if(a<0||b<0||c<0||a>200||b>200||c>200) System.out.println("Wrong Format"); else if(a+b<=c||a+c<=b||b+c<=a) System.out.println("Not a triangle"); else if(a==b&&a==c&&c==b) System.out.println("Equilateral triangle"); else if(a*a+b*b-c*c<1e-6||a*a+c*c-b*b<1e-6||b*b+c*c-a*a<1e-6) { if(a==b||a==c||c==b) System.out.println("Isosceles right-angled triangle"); else System.out.println("Right-angled triangle"); } else if(a==b||a==c||c==b) System.out.println("Isosceles triangle"); else System.out.println("General triangle"); } }
设计思想
大方向:一般三角形 a+b>c 这是大前提条件等边三角形 ;a=b=c等腰直角三角形a=b, a×a+b×b=c×c;等腰三角形 a=b;直角三角形 a×a+b×b=c×c
根据题目要求,我们首先要判断输入的数据是否合法。然后按三角形类型的判断条件范围从大到小进行判断。
问题
题目中遇到的问题:
参考题目中提供的给出输入与输出,过不了的测试用例其实是等腰直角三角形,因为直角边和斜边满足根2倍数关系,根2是无限小数,所以题目中在给用例的时候无法给出一个准确值,因此不能用平方和的方法判断。应该用直角边的根2倍与斜边的差值绝对值是否足够小。
解决方案
解决直角边的根2倍与斜边的差值的差的绝对值是否足够小的问题,我用的是是否小于1e-6c(1x10^(-6)),所以是在做不出来的时候可以从出题者角度思考问题,或许会有收获。很多牵扯到无限小数的场景,都会用差值绝对值是否小于某个值来近似看做相等,这也是一种重要的编程思想。
题目集2的7-4
输入年月日的值(均为整型数),输出该日期的下一天。 其中:年份的合法取值范围为[1820,2020] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。 注意:不允许使用Java中和日期相关的类和方法。
要求:Main类中必须含有如下方法,签名如下:
public static void main(String[] args);//主方法
public static boolean isLeapYear(int year) ;//判断year是否为闰年,返回boolean类型
public static boolean checkInputValidity(int year,int month,int day);//判断输入日期是否合法,返回布尔值
public static void nextDate(int year,int month,int day) ; //求输入日期的下一天
输入格式:
在一行内输入年月日的值,均为整型数,可以用一到多个空格或回车分隔。
输出格式:
当输入数据非法及输入日期不存在时,输出“Wrong Format”;
当输入日期合法,输出下一天,格式如下:Next date is:年-月-日
输入样例1:
在这里给出一组输入。例如:
2020 3 10
输出样例1:
在这里给出相应的输出。例如:
Next date is:2020-3-11
输入样例2:
在这里给出一组输入。例如:
2025 2 10
输出样例2:
在这里给出相应的输出。例如:
Wrong Format
源代码:
import java.util.Scanner; public class Main { public static void main(String[] args) { Main cal=new Main(); Scanner input=new Scanner(System.in); int year=input.nextInt(); int month=input.nextInt(); int day=input.nextInt(); if(cal.checkInputValidity(year,month,day)==false) System.out.println("Wrong Format"); else cal.nextDate(year,month,day); } public static boolean isLeapYear(int year) { if (year%400==0||(year%4==0&&year%100!= 0)) return true; else return false; } public static boolean checkInputValidity(int year,int month,int day) { if(year<1820||year>2020||month<1||month>12||day<1||day>31||(isLeapYear(year)==false&&month==2&&day==29)) return false; else return true; } public static void nextDate(int year,int month,int day) { if(isLeapYear(year)) { if(((month==1||month==3||month==5||month==7||month==8||month==10)&&day==31)||((month==4||month==6||month==9||month==11)&&day==30)||(month==2&&day==29)) {month=month+1; day=1;} else if(month==12&&day==31) { year=year+1; month=1; day=1; } else day=day+1; } else { if(((month==1||month==3||month==5||month==7||month==8||month==10)&&day==31)||((month==4||month==6||month==9||month==11)&&day==30)||(month==2&&day==28)) {month=month+1; day=1;} else if(month==12&&day==31) { year=year+1; month=1; day=1; } else day=day+1; } System.out.println("Next date is:"+year+"-"+month+"-"+day); } }
设计思想
根据题目中给定的Main类中存在的方法,分别设计出每个方法所实现的功能。首先要判断出输入的数据是否合法,如果输入合法先行判断输入的年份是否为闰年,在求下一天的方法中求下一天分为两种情况,一种是闰年一种是非闰年,使用if-else语句对两种情况分别求下一天。
题目集2的7-5
输入年月日的值(均为整型数),同时输入一个取值范围在[-10,10] 之间的整型数n,输出该日期的前n天(当n > 0时)、该日期的后n天(当n<0时)。
其中年份取值范围为 [1820,2020] ,月份取值范围为[1,12] ,日期取值范围为[1,31] 。
注意:不允许使用Java中任何与日期有关的类或方法。
输入格式:
在一行中输入年月日的值以及n的值,可以用一个或多个空格或回车分隔。
输出格式:
当输入的年、月、日以及n的值非法时,输出“Wrong Format”;
当输入数据合法时,输出“n days ago is:年-月-日”
输入样例1:
在这里给出一组输入。例如:
2018 6 19 8
输出样例1:
在这里给出相应的输出。例如:
8 days ago is:2018-6-11
输入样例2:
在这里给出一组输入。例如:
2018 6 19 -8
输出样例2:
在这里给出相应的输出。例如:
-8 days ago is:2018-6-27
源代码:
import java.util.Scanner; public class Main { public static void main(String[] args) { Main cal=new Main(); Scanner input=new Scanner(System.in); int year=input.nextInt(); int month=input.nextInt(); int day=input.nextInt(); int n=input.nextInt(); if(cal.checkInputValidity(year,month,day,n)==false) System.out.println("Wrong Format"); else if(day>n) { day=day-n; if(isLeapYear(year)==true&&month==2&&day>29) { month=3; day=day-29; } else if(isLeapYear(year)==false&&month==2&&day>28) { month=3; day=day-28; } else if(month==12&&day>31) { year=year+1; month=1; day=day-31; } else if((month==4||month==6||month==9||month==11)&&day>30) { month=month+1; day=day-30; } else if((month==3||month==5||month==7||month==8||month==10||month==12)&&day>31) { month=month+1; day=day-31; } System.out.println(n+" days ago is:"+year+"-"+month+"-"+day); } else if(day<=n) { if(month==1) { year=year-1; month=12; day=31+day-n; } else if(month==3) { if(isLeapYear(year)==true) { month=2; day=29+day-n; } else { month=2; day=28+day-n; } } else if(month==5||month==7||month==10||month==12) { month=month-1; day=30+day-n; } else { month=month-1; day=31+day-n; } System.out.println(n+" days ago is:"+year+"-"+month+"-"+day); } } public static boolean isLeapYear(int year) { if (year%400==0||(year%4==0&&year%100!= 0)) return true; else return false; } public static boolean checkInputValidity(int year,int month,int day,int n) { if(year<1820||year>2020||month<1||month>12||day<1||day>31||n<-10||n>10||(isLeapYear(year)==false&&month==2&&day==29)) return false; else return true; } }
设计思想
7-5与题目7-4大体上是一样的,差别不大。同样是对输入的数据进行判是否为闰年以及判断输入是否合法。我们对大月分以及小月份的月份数和月份内的天数进行赋值。首先判断是否为闰年,其次是n的正负问题,要求解的是前n天还是后n天。如果求后n天,用输入的day减n得新的day,判断月份是否为12月,是12月且day大于31,年份加一,月份加一,day-31;如果不是判断得到的day是否大于所在月的最大天数,如果大于月份的最大天数,只是年份不变,月份加一,day的值为得到的day减月份的最大天数。求前n天的日期是同样的道理。
问题
输出时先得到月份,减该月最大天数的时候得出的day错误。
解决方案
先求出day的值,通过day求得month的值。
题目集3的7-2
定义一个类Date,包含三个私有属性年(year)、月(month)、日(day),均为整型数,其中:年份的合法取值范围为[1900,2000] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。 注意:不允许使用Java中和日期相关的类和方法,否则按0分处理。
要求:Date类结构如下图所示:
输入格式:
在一行内输入年月日的值,均为整型数,可以用一到多个空格或回车分隔。
输出格式:
当输入数据非法及输入日期不存在时,输出“Date Format is Wrong”;
当输入日期合法,输出下一天,格式如下:Next day is:年-月-日
输入样例1:
在这里给出一组输入。例如:
1912 12 25
输出样例1:
在这里给出相应的输出。例如:
Next day is:1912-12-26
输入样例2:
在这里给出一组输入。例如:
2001 2 30
输出样例2:
在这里给出相应的输出。例如:
Date Format is Wrong
源代码:
import java.util.Scanner; public class Main { public static void main(String[] args) { // TODO Auto-generated method stub Scanner input = new Scanner(System.in); int year = input.nextInt(); int month = input.nextInt(); int day = input.nextInt(); int[] days = new int[] {0,31,28,31,30,31,30,31,31,30,31,30,31}; Date date = new Date(year,month,day); date.nextDate(year,month,day); } } class Date { private int year; private int month; private int day; public Date() { } public Date(int year, int month, int day) { // TODO Auto-generated constructor stub this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getday() { return day; } public void setDay(int day) { this.day = day; } public static boolean isLeapYear(int year) { if (year%400==0||(year%4==0&&year%100!= 0)) return true; else return false; } public static boolean checkInputValidity(int year,int month,int day) { if(year<1900||year>2000||month<1||month>12||day<1||day>31||(isLeapYear(year)==false&&month==2&&day==29)) return false; else return true; } public static void nextDate(int year,int month,int day) { //求下一天注意夸年月以及特殊月份以及闰年和平年的二月不同天数 int[] days=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; if(isLeapYear(year)) days[2] = 29; int a = 0,b = 0,c = 0; if(checkInputValidity(year,month,day)) { if(month==12) { if(day==days[month]) { a = year+1; b = 1; c = 1;} if(day>0&&day<days[month]) {a = year; b = month; c =day +1; } } if(month<12) { if(day==days[month]) { a = year; b = month + 1; c = 1;} if(day>0&&day<days[month]) {a = year; b = month; c = day+1; } } System.out.println("Next day is:"+a+"-"+b+"-"+c); } else System.out.println("Date Format is Wrong"); } }
设计思路
根据题目中给定的UML类图来设计此题,创建Date类,,初始化Date类的数据域,注意类方法的输入参数与返回值,求下一天的方法同题目集2中7-4的方法一样,在Main方法中通过date.nextDate(year,month,day)语句来获取下一天。
题目集3的7-3
编写程序性,实现对简单多项式的导函数进行求解。详见作业指导书。 OO作业3-3题目说明.pdf
输入格式:
在一行内输入一个待计算导函数的表达式,以回车符结束。
输出格式:
如果输入表达式不符合上述表达式基本规则,则输出“Wrong Format”。
如果输入合法,则在一行内正常输出该表达式的导函数,注意以下几点: 结果不需要排序,也不需要化简;
当某一项为“0”时,则该项不需要显示,但如果整个导函数结果为“0”时,则显示为“0”;
当输出结果第一项系数符号为“+”时,不输出“+”;
当指数符号为“+”时,不输出“+”;
当指数值为“0”时,则不需要输出“x^0”,只需要输出其系数即可。
输出格式见输入输出示例。
输入样例1:
在这里给出一组输入。例如:
-2* x^-2+ 5*x^12-4*x+ 12
输出样例1:
在这里给出相应的输出。例如:
4*x^-3+60*x^11-4
输入样例2:
在这里给出一组输入。例如:
2*x^6-0*x^7+5
输出样例2:
在这里给出相应的输出。例如:
Wrong Format
源代码:
import java.math.BigInteger; import java.util.LinkedList; import java.util.List; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); String data = in.nextLine(); data = data.replaceAll(" ", ""); Polynomial polynomial = new Polynomial(data); if(data.matches("[-]?[0-9]+")) { //如果输入为常数 System.out.println("0"); } else if(!polynomial.isPolynomial(data) || data.isEmpty()) { //判断正确,判断为空 System.out.println("Wrong Format"); } else { //first List<String> list = new LinkedList<String>(); List<String> list1 = new LinkedList<String>(); list = polynomial.getPolynomial(); //得到每一项 for(int i = 0 ; i < list.size() ; i++) { if(polynomial.isInteger(list.get(i).toString())) { //如果是一个常数 } else { //second 是式子了话 BigInteger coeff = polynomial.getCoeff(list.get(i).toString()); //若不是常数求系数和指数 BigInteger index = polynomial.getIndex(list.get(i).toString()); BigInteger value = new BigInteger("0"); PowerFunctionTerm powerFunctionTerm = new PowerFunctionTerm(coeff,index); String s1 = powerFunctionTerm.getDerivative().getCoeff()+""; //新系数 String s2 = powerFunctionTerm.getDerivative().getIndex()+""; //新指数 //if(i != 0) { //不是第一位+-号情况 Special special = new Special(s1,s2,i); list1.add(special.index1().replaceAll(" ", "")); } // second } Expression expression = new Expression(list1); for(int i = 0 ; i <expression.list.size() ; i++) { System.out.print(expression.list.get(i)); } } // first } } //下面是其他类 class PowerFunctionTerm extends Term { //待指数求导并返回值——字符串 private BigInteger coeff; //系数 private BigInteger index; //指数 public PowerFunctionTerm(BigInteger coeff, BigInteger index) { this.coeff = coeff; this.index = index; } public BigInteger getCoeff() { return coeff; } public void setCoeff(BigInteger coeff) { this.coeff = coeff; } public BigInteger getIndex() { return index; } public void setIndex(BigInteger index) { this.index = index; } public PowerFunctionTerm getDerivative() { //这里求常数单项的导数 BigInteger a = new BigInteger("1"); return new PowerFunctionTerm(coeff.multiply(index),index.subtract(a)); } public String toString() { return getDerivative().toString(); } } class ConstantTerm extends Term { //常数求导并返回值——字符串 private BigInteger value; //常数值 public ConstantTerm(BigInteger value) { this.value = value; } public BigInteger getValue() { return value; } public void setValue(BigInteger value) { this.value = value; } public Term getDerivative() { //这里求常数单项的导数 value = new BigInteger("0"); return new ConstantTerm(value); } public String toString() { //把求导后的数转化为字符串 return getDerivative().toString(); } } class Expression { //用来把求导好的字符串输出 List<String> list = new LinkedList<String>(); public Expression(List<String> list) { this.list = list; } public String toString() { return list.toString(); } } class Polynomial { private String data; public Polynomial(String data) { this.data = data; } public List<String> getPolynomial(){ List<String> list = new LinkedList<String>(); String[] arr = new String[1000]; String str = ""; int flag = 0; //-2*x^-2+5*x^12-4*x+12 int flag1 = 0; for(int i = 0 ; i < data.length() ; i++) { String a = String.valueOf(data.charAt(i)); if(a.equals("-") || a.equals("+")) { if(i == 0) { if(a.equals("-")) //当负号为第一位的时候可以保留 str = str+a; } else { String lowa = String.valueOf(data.charAt(i-1)); String longa = String.valueOf(data.charAt(i+1)); if(lowa.equals("^")) str = str+a; else if(lowa.matches("[0-9]+") || longa.matches("[0-9]+")) { //jia[]+ if(a.equals("-")) { flag1 = 1; } flag = 1; } }//这个if-else是判断为负号时是否保留 } else { str = str+a; } if(flag == 1) { list.add(str); flag = 0; str = ""; if(flag1 == 1) { str = "-"+str; flag1 = 0; } } if(i == data.length()-1) { list.add(str); } } return list; } public boolean isPolynomial(String s) { //判断是不是多项式,符合进行拆分 boolean judge = true; if(!s.matches("([+-]?([1-9][0-9]*)?((\\*)?x(\\^[+-]?[1-9][0-9]*)?)?)+")) { judge = false; //"^([-+]?([1-9][0-9]*)?(\\*)?(x(\\^[+-]?[0-9]+)?)?)+$" } return judge; } public boolean isInteger(String s) { //判断是不是多项式 boolean judge = true; for(int i = 0 ; i < s.length(); i++) { if(String.valueOf(s.charAt(i)).compareTo("x") == 0) { judge = false; break; } } return judge; } public BigInteger getIndex(String s) { String[] test = s.split("\\^"); if(test[0].compareTo(s) == 0) { if(test[0].endsWith("x")) { test[0] = "1"; } } else { test[0] = test[1]; } return new BigInteger(test[0]); } public BigInteger getCoeff(String s) { String[] test = s.split("\\*"); if(test[0].compareTo(s) == 0) { if(String.valueOf(test[0].charAt(0)).compareTo("x") == 0) { test[0] = "1"; } else { test[0] = "-1"; } } return new BigInteger(test[0]); } } abstract class Term { public abstract Term getDerivative(); //用来求每个单项 } class Special { String s1,s2; int i; public Special(String s1,String s2,int i) { this.s1 = s1; this.s2 = s2; this.i = i; } public String index1() { // String end = ""; if(s1.equals("1")&&s2.equals("0")) end = "1"; else if(s1.equals("1")&&s2.equals("1")) end = "x"; else if(s1.equals("-1")&&s2.equals("0")) end = "-1"; else if(s1.equals("-1")&&s2.equals("1")) end = "-x"; else if(s1.equals("1")) end = "x^"+s2 ; else if(s1.equals("-1")) end = "-x^"+s2; else if(s2.equals("0")) end = s1; else if(s2.equals("1")) end = s1+"x"; else { if(s1.compareTo("0") > 0 && i != 0) end = "+"+s1+"*x^"+s2; else if(s1.compareTo("0") > 0 && i == 0) end = s1+"*x^"+s2; else if(s1.compareTo("0") < 0) end = s1+"*x^"+s2; } return end; } }
设计思路
这道题还是有着很鲜明的特点,拿到这道题的时候脑子中没有任何思路,后来通过查阅资料时看到了这张类图,有了解题的思路。这说明我在初期的设计中有着明显的能力不足,导致我在看到这道题是没有任何思路。
首先要学会正则表达式对输入的多项式进行判断是否为合法输入,然后设计待指数求导并返回值------字符串的类,常数项类、幂函数类返回值都为常数项来继承Term类中的求导方法来求每个单项的导数,最后是对多项式中特殊情况的处理,单独设计了Special类来对特殊情况进行处理,特殊情况一般为系数或是指数为1,0之类的情况的处理。文中的每部分代码中都有一些相对总结性的注释。
问题
对于题目中设置的测试节点,最后两个大数据测试无法通过,以及无法判断第一项系数为负数的多项式,不可以使用int型来存储数据。需要使用大数类型来存储数据。目前遇到的问题还待解决。希望可以在后期的讲解中找到解决方案。
作业总结
通过这几次的作业效果来看,我编写的代码对于老师设定的测试节点通过率不高,而且设计的代码在细节上还是很繁琐的,整体结构不够简洁,代码的可阅读性不高,我的编写能力在这几方面还有待提高。通过这几次作业强化我在类的构造方法、方法的调用、参数传递、对象的构造与使用;练习循环结构、控制结构;练习数据的输入与输出;学习编写结构清晰、逻辑正确、功能完善的java代码。学习使用工具对代码进行跟踪调试。理解抽象类与子类的关系,实现继承操作,构造类族;正确使用抽象类和子类实现多态操作;理解接口的意义,并实现行为的多态;理解了解抽象类、类以及接口三者的异同以及使用场合。掌握UML基本用法和画法,并能根据题目描述画出类、接口、类等方面的提高。希望在后期的讲解中找到待解决问题的解决方法。