面向对象程序设计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+ 5x^12-4x+ 12
输出样例1:
在这里给出相应的输出。例如:
4*x-3+60*x11-4
输入样例2:
在这里给出一组输入。例如:
2*x6-0*x7+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基本用法和画法,并能根据题目描述画出类、接口、类等方面的提高。希望在后期的讲解中找到待解决问题的解决方法。