关于OOP第一阶段的作业总结
阅读提示:本文目录导航,PC端页面位于文章右侧,移动端点击文章标题右侧小图标,需下滑后(顶部图片消失)目录才完全显示,可根据目录跳转文章位置。
点击跳转至本文唯一价值 也可能没有价值(雾 其他的都是在完成学校任务,基本语法没什么好总结的
一、前言
以下题目来源PTA题目集1-3,基本为Java的基本语法及一点面向对象的思想。
- 知识点:Java基本语法
(没什么好讲的),正则表达式
- 题量:适中
- 难度:除题目集7-3 一元多项式求导 无
二、设计与分析
1. 题目集一
7-8 判断三角形类型(20 分)
输入三角形三条边,判断该三角形为什么类型的三角形。
输入格式:
在一行中输入三角形的三条边的值(实型数),可以用一个或多个空格或回车分隔,其中三条边的取值范围均为[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”。
分析与心得: 先给输入的三条边排好序,可以减少很多判断。 使用\(x^2 + y^2 = z^2\)判断直角三角形时,若为浮点数会出现浮点误差无法相等。如\(\sqrt2\), 则差在精度范围内即可认为相等。
核心代码:
Arrays.sort(arr);
final double EPS = 1e-5;//写-8 不行 这精度卡的。。。
if (arr[0] < 1 || arr[2] > 200)
System.out.print("Wrong Format");
else if (arr[0] + arr[1] <= arr[2])
System.out.print("Not a triangle");//wow 看错题了,还以为和上一行输出一样
else if (arr[0] == arr[1] && arr[1] == arr[2])
System.out.print("Equilateral triangle");
else if (arr[0] == arr[1] || arr[1] == arr[2]) {
if (Math.abs(arr[0] * arr[0] + arr[1] * arr[1] - arr[2] * arr[2]) <= EPS)//卡精度??????
System.out.print("Isosceles right-angled triangle");
else
System.out.print("Isosceles triangle");
} else if (Math.abs(arr[0] * arr[0] + arr[1] * arr[1] - arr[2] * arr[2]) <= EPS)
System.out.print("Right-angled triangle");
else
System.out.print("General triangle");
2. 题目集二
7-4 求下一天 (30 分)
输入年月日的值(均为整型数),输出该日期的下一天。 其中:年份的合法取值范围为[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:年-月-日
分析与心得: 注意各月的天数需严格符合实际,求下一天时注意边界进制。
核心代码:
public static void nextDate() {
int[] monthDay = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (isLeapYear(year))
monthDay[2]++;
if (day < monthDay[month])
day++;
else if (day == monthDay[month] && month < 12) {
month++;
day = 1;
} else if (day == monthDay[month] && month == 12) {
year++;
month = 1;
day = 1;
}
}
7-5 求前N天 (30 分)
输入年月日的值(均为整型数),同时输入一个取值范围在[-10,10] 之间的整型数n,输出该日期的前n天(当n > 0时)、该日期的后n天(当n<0时)。
其中年份取值范围为 [1820,2020] ,月份取值范围为[1,12] ,日期取值范围为[1,31] 。
注意:不允许使用Java中任何与日期有关的类或方法。
输入格式:
在一行中输入年月日的值以及n的值,可以用一个或多个空格或回车分隔。
输出格式:
当输入的年、月、日以及n的值非法时,输出“Wrong Format”;
当输入数据合法时,输出“n days ago is:年-月-日”
分析与心得: 注意各月的天数需严格符合实际,求前n天时注意边界进制,跨月。
核心代码:
public static void beforeDate(int n) {
int[] monthDay = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (isLeapYear(year))
monthDay[2]++;
if ((day - n) <= monthDay[month] && (day - n) >= 1) {
day = day - n;
} else if ((day - n) > monthDay[month] && month < 12) {
day = day - n - monthDay[month];
month++;
} else if ((day - n) < 1 && month > 1) {
month--;
day = day - n + monthDay[month];
} else if ((day - n) > monthDay[month] && month == 12) {
day = day - n - monthDay[month];
month = 1;
year++;
} else if ((day - n) < 1 && month == 1) {
month = 12;
day = day - n + monthDay[month];
year--;
}
}
3. 题目集三
7-2 定义日期类 (28 分)
定义一个类Date,包含三个私有属性年(year)、月(month)、日(day),均为整型数,其中:年份的合法取值范围为[1900,2000] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。 注意:不允许使用Java中和日期相关的类和方法,否则按0分处理。
要求:Date类结构如下图所示:

输入格式:
在一行内输入年月日的值,均为整型数,可以用一到多个空格或回车分隔。
输出格式:
当输入数据非法及输入日期不存在时,输出“Date Format is Wrong”;
当输入日期合法,输出下一天,格式如下:Next day is:年-月-日
分析与心得: 注意类的设计,开始有一点面向对象了。同样需要注意边界控制和进制处理。
核心代码:
class Date {
private int year;
private int month;
private int day;
private int[] mon_maxnum = new int[] { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
Date() {
}
Date(int year, int month, int day) {
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 boolean isLeapYear(int year) {
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
return true;
else
return false;
}
public boolean checkInputValidity() {
if (year < 1900 || year > 2000 || month < 1 || month > 12 || day < 1 || day > 31) {
return false;
}
if (month == 2) {
if (isLeapYear(year) && day > 29) {
return false;
}
else if(!isLeapYear(year) && day > 28)
return false;
}
if ((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) {
return false;
}
return true;
}
public void getNextDate() {
if (isLeapYear(year))
mon_maxnum[2]++;
if (day < mon_maxnum[month])
day++;
else if (day == mon_maxnum[month] && month < 12) {
month++;
day = 1;
} else if (day == mon_maxnum[month] && month == 12) {
year++;
month = 1;
day = 1;
}
}
}
本文唯一价值。
7-3 一元多项式求导(类设计) (50 分)
编写程序性,实现对简单多项式的导函数进行求解。详见作业指导书。
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
分析与心得:
本题前置知识正则表达式
了解个大概的基础语法就行
求导大家高中都学过,对于幂函数(本题只有幂函数及常数),无非就三个部分,系数,底数,指数。(博客园的\(\LaTeX\)支持还行)
\[\begin{cases}
新系数 = 旧系数 \times 旧指数\新指数 = 旧指数 - 1\\
\end{cases}
\]
所以,我们对于表达式的每一项提取出该项的系数和指数。
根据系数和指数的位置改进正则表达式如下:(当时我正则表达式调了挺久)
先把字符串内的空格都去了:
String express = initExpress.replaceAll("\\s", "");
当指数和系数都存在(指不为 1 的其他数字 下同):
[+-]?[0-9]+\\*x\\^[+-]?[0-9]+
这种表达式能匹配上诸如 -5*x^15
类型。
兼容无前导0,就是数字不能0开头,(这个pdf说明内有要求,但我看有些同学没考虑这也过了,建议加强测试数据):
[+-]?[1-9][0-9]*\\*x\\^[+-]?[1-9][0-9]*
诸如09*x^02
就无法匹配上。
兼容系数不存在:
([+-]?[1-9][0-9]*)?\\*?x\\^[+-]?[1-9][0-9]*
如-x^5
。
兼容指数不存在:
([+-]?[1-9][0-9]*)?\\*?x(\\^[+-]?[1-9][0-9]*)?
如-x
。
兼容常数项:
([+-]?[1-9][0-9]*)?(\\*?x(\\^[+-]?[1-9][0-9]*)?)?
如445544
给指数部分加上()方便提取:
([+-]?[1-9][0-9]*)?(\\*?x(\\^([+-]?[1-9][0-9]*))?)?
提取使用group(index),index为第几个括号
如group(1)得到的就是系数部分,即匹配([+-]?[1-9][0-9]*)
的部分。
group(4)得到指数部分,即匹配([+-]?[1-9][0-9]*)
的部分。
有大数数据类型要使用BigInteger
完整的用来匹配项的正则表达式:
String termExpress = "([+-]?[1-9][0-9]*)?" + "(\\*?[+-]?x(\\^([+-]?[1-9][0-9]*))?)?";
那么多项表达式(表达式由项组成)及
String wholeExpress = "(([+-]?[1-9][0-9]*)?" + "(\\*?[+-]?x(\\^([+-]?[1-9][0-9]*))?)?)+";
能匹配则表达式合法:
public boolean check() {
return Pattern.matches(wholeExpress, express);
}
hack数据及其他详见踩坑心得
deadline驱动,未经检查,欢迎指正错误。
完整代码:
//并非AC代码,测试点测试数据有误,AC需第88行(如果没修正的话)。
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String initExpress = input.nextLine();
String express = initExpress.replaceAll("\\s", "");
Express textExpress = new Express(express);
if (textExpress.check())
textExpress.show();
else
System.out.println("Wrong Format");
}
}
class Express {
private String express;
private String term;
private String ratStr;// 系数部分
private String indexStr;// 指数部分
private BigInteger rat;
private BigInteger index;
private ArrayList<String> containTerm = new ArrayList<>();
public Express(String express) {
this.express = express;
}
String termExpress = "([+-]?[1-9][0-9]*)?" + "(\\*?[+-]?x(\\^([+-]?[1-9][0-9]*))?)?";
String wholeExpress = "(([+-]?[1-9][0-9]*)?" + "(\\*?[+-]?x(\\^([+-]?[1-9][0-9]*))?)?)+";
String constNum = "[+-]?[0-9]+";
public boolean check() {
return Pattern.matches(wholeExpress, express);
}
public void show() {
if (Pattern.matches(constNum, express)) {
System.out.println("0");
System.exit(0);
}
Pattern p = Pattern.compile(termExpress);
Matcher m = p.matcher(express);
int flag = 0;
while (m.find()) {
//System.out.println(m.group(2));
ratStr = m.group(1);
indexStr = m.group(4);
if (ratStr != null) {
rat = new BigInteger(ratStr);
if (m.group(2) != null && m.group(2).startsWith("-")) {
rat = BigInteger.valueOf(-1);
//System.out.println(rat);
}
else if (m.group(2) != null && m.group(2).startsWith("+")) {
rat = BigInteger.valueOf(1);
//System.out.println(rat);
}
} else {
rat = BigInteger.valueOf(1);
if (m.group() != null && m.group().startsWith("-")) {
rat = BigInteger.valueOf(-1);
//System.out.println(rat);
}
else if (m.group() != null && m.group().startsWith("+")) {
rat = BigInteger.valueOf(1);
//System.out.println(rat);
}
}
if (indexStr != null) {
index = new BigInteger(indexStr);
rat = rat.multiply(index);
index = index.subtract(new BigInteger("1"));
if (rat.compareTo(new BigInteger("0")) > 0 && flag == 1)
System.out.print("+" + (rat.equals(new BigInteger("-1")) ? "-x" : rat + "*x") + (index.equals(new BigInteger("1")) ? "" : ("^" + index)));
else {
flag = 1;
System.out.print((rat.equals(new BigInteger("-1")) ? "-x" : rat + "*x") + (index.equals(new BigInteger("1")) ? "" : ("^" + index)));
}
} else if (m.group(2) != null) {
if(flag == 1 && rat.compareTo(new BigInteger("0")) > 0)
System.out.print("+"+rat);
////emmmmmmmmmmmmmmmmmmmmmmm
//测试点数据错误........................................
//System.out.print(rat);
else{
flag = 1;
System.out.print(rat);
}
}
}
}
}
三、踩坑心得
题目集7-3 一元多项式求导
踩坑完善过程部分可见 设计与分析
特殊数据考虑不完善
hack数据1:
\(5-x\)
正确输出:
\(-1\)
hack数据2:
2-x^2
正确输出:
\(-2x\)
hack数据3:
-x^
正确输出:
\(Wrong \quad Format\)
hack数据4:
-x ^-1
正确输出:
x^-2
一直没满分,一直捏数据hack自己,确实完善了一些特殊情况。
\(\frac{写题时间}{hack时间} = \frac{2}{8}\)

但一直过不了最后一个点。
结果发现是最后一个测试点的测试数据有误。
求完导后,如果答案项系数为+ 且不是第一项应该在前面输出 + 号。
如5*x^4+4*x^5
带+
答案: 20x^3+20x^4
不带+
答案: 20x^320x^4
对于+是否输出
该测试点: \(输出× \quad 不输出√\) ??
具体请见上文完整代码第85行和第88行(测试点85行错 88行对)
四、改进建议
对于题目集三一元多项式求导,仍有太多面向过程的做法,冗余代码过多,复用性低,不利于后期跟进维护。
优化:设计项类,将表达式分解成项,对项进行处理,使用 HashMap建立映射关系,利于运算,利于合并同类项(本题降低了难度并未涉及),降低程序耦合度。
五、总结
学到了什么:通过本阶段的学习,练习了Java的基本语法,了解了面向对象的思想,练习了正则表达式的使用。
进一步学习:不停留在基本语法,开始了解各种开发框架和管理工具。
课程建议:课程自由度高,给学生充分的自学时间,作业循序渐进,梯度适中,无需要改进的地方。
另:关于题目集三最后一题<一元多项式求导>最后一个测试点测试数据有误,望修改,建议加强测试数据,详见
测试数据出锅分析
和分析与设计