PTA题目集1~3总结
一、前言:总结三次题目集的题量、知识点、难度等情况
第一次的题目集有8道题:
7-1 计算两个数的和:简单的输入输出。
7-2 电话键盘字母数字转换:对字符的初步认识。
7-3 成绩分级管理:及if语句的基本掌握 。
7-4 计算税率:细心与耐心的考察。
7-5 计算钱币:对算法的思考 。
7-6 使用一维数组求平均值:数组初识。
7-7 对多个整数进行排序:数组进一步的使用。
7-8 判断三角形类型:对前面考察的能力进行综合考察。
小总结:第一次题目集并不难,对于有C语言基础的我能快速上手,目的是为了让我们从C语言过渡到java,让我们初步认识java。
第二次的题目集有5道题:
7-1 IP地址转换:认识不同进制以及不同进制之间的关系。
7-2 合并两个有序数组为新的有序数组:数组合并与排序,对数组进一步的掌握。
7-3 判断闰年及星期几 :方法的书写与调用,细分任务给方法,以及各种特殊情况的全方面考虑。
7-4 求下一天:同上。
7-5 求前N天:同上。
小总结:相比第一次题目集,第二次题目集的难度要大了很多,不过都是换汤不换药,有C语言的基础,java的方法可以类比于C语言的函数与过程,所以还是容易融会贯通的。
第三次的题目集有3道题:
7-1 创建账户类Account:认识类
7-2 定义日期类:强化类的用法
7-3 一元多项式求导(类设计):正则判断的掌握,大数据的用法
小总结:这次的题集没有拿到满分,由此可见,这次的题集对我来讲是有难度的,尤其是最后一道求导题。类这个知识对我来说是全新的,原先在C语言中没遇见过,不过通过自学掌握了它之后发现它有点像C语言的结构体,如果能很好地利用它,帮助效果将会很大。还有体会到了第三题的正则表达式的“复杂与强大”。
二、设计与分析:分享其中的几道题目的设计与分析
(1)题集一——7-8 判断三角形类型:
设计: 1.读取三角形三边数据 --> 2.把数据存入数组并给数组排序 --> 3.if语句判断条件 --> 4.根据判断结果输出
分析: 一开始拿到这道题的时候,容易盲目地进行if语句的判断与输出,然后会发现这样写出来的代码很拖拉繁长,不干净,层次不清,
容易把自己绕得晕晕乎乎的。后来发现把它们存入数组并排序后再判断,可以在if语句那里省去对很多的情况判断,这样一来,
if语句会简洁明了很多。
代码如下:
1 import java.util.Scanner; 2 import java.util.*; 3 public class Main { 4 5 public static void main(String[] args) { 6 // TODO 自动生成的方法存根 7 Scanner in = new Scanner(System.in); 8 int i; 9 double a = in.nextDouble(); 10 double b = in.nextDouble(); 11 double c = in.nextDouble(); 12 13 double[] edge=new double[3]; 14 edge[0]=a; 15 edge[1]=b; 16 edge[2]=c; 17 // 数组的排序函数 18 Arrays.sort(edge); 19 a=edge[0]; 20 b=edge[1]; 21 c=edge[2]; 22 23 if(a >= 1 && a<=200 && b>=1&& b<=200&& c>=1&&c<=200){ 24 25 if(a+b> c) 26 { 27 if(a == b && b == c)//等边三角形 28 System.out.println("Equilateral triangle"); 29 else if(a==b&&a*a+b*b-c*c<0.00001)//等腰直角三角形 31 System.out.println("Isosceles right-angled triangle"); 32 else if(a == b||b == c||a == c)//一般等腰三角形 33 System.out.println("Isosceles triangle"); 34 else if(a*a+b*b-c*c<0.00001)//一般直角三角形 35 System.out.println("Right-angled triangle"); 36 else //一般三角形 37 System.out.println("General triangle"); 38 } 39 40 else //不能构成三角形 41 System.out.println("Not a triangle"); 42 } 43 else //输入有误 44 System.out.println("Wrong Format"); 45 } 46 }
(2)题集二——7-4 求下一天 :
设计: 1.读取年月日 --> 2.判断日期是否合法 --> 3.通过 “判断year是否为闰年,返回boolean类型”、“判断月份”、“求日期下一天” 这三个方法相互配合输出结果。
分析: 该题目难就难在不停地要考虑一些特殊情况,比如是否为闰年,该月是否是二月,是28天还是29天,该天该月是不是边界(下一天是另一个月下一月是另一年)等等,
如果没考虑好的话代码容易出bug就要不停地添加判断条件。所以,最好能将判断的代码细分到几个方法中,在主函数里通过对方法的调用让代码思路清晰、简洁明了。
不过这次方法部分的代码的书写还是比较乱,有很大的改进空间。
下面是我的主函数的代码:
public static void main(String[] args) { // TODO 自动生成的方法存根 Scanner in = new Scanner(System.in); int year, month, day;
//读取年月日 year = in.nextInt(); month = in.nextInt(); day = in.nextInt(); //判断加输出 if (checkInputValidity(year, month, day)) nextDate(year, month, day); else System.out.println("Wrong Format"); }
(3)题集二——7-5 求前N天:
设计: 1.读取年月日及n --> 2.判断日期和n是否合法 --> 3.分开考虑是否为闰年的情况 --> 4.进行判断加输出
分析: 现在的我来这次的代码是满满的嫌弃,以至于看着它不知道如何概括它的设计思路,就觉得条理不清晰,分工不明确,感觉当时的自己就是为了完成任务而写的。
这道题是上一道题的升级版,考虑的地方都大同小异,不过这道题我换了一种思路,我是先把日期计算成它是该年的第几天,然后再对它加减天数,然后在转化为年月日的形式,
不过这样还是要单独考虑年边界的情况。这一题真的是对我们细心和耐心的考验。
下面是题集二7-4 7-5都用到的判断方法的代码:
// 判断输入日期是否合法,返回布尔值 public static boolean checkInputValidity(int year, int month, int day) { if (year >= 1820 && year <= 2020 && month >= 1 && month <= 12 && day >= 1 && day <= 31) { if ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) return false; else if (month == 2 && day > 28 && (isLeapYear(year) == false)) return false; else if (month == 2 && isLeapYear(year) && day > 29) return false; else return true; } else return false; }
// 判断year是否为闰年,返回boolean类型; public static boolean isLeapYear(int year) { if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)) return true; else return false; }
(4)题集二——7-1 创建账户类Account
设计: 创建一个类,属性(数据域)有id、balance、annualInterestRate、dateCreated;按题目要求在类里面创建相应的方法--> 在主函数里定义并初始化一个该类的对象 --> 在主函 数里调用对象的方法输出结果。
分析: 写这题目的时候刚刚接触类,还不能懂一些代码的含义和意图,但是这道题的要求给得很详细感觉在教我们怎么写这道题,就照着书上的样例,照猫画虎地写下了如下的代码。
不过完成这道题后,我对类有了一个大概的掌握。
1 import java.time.LocalDate ; 2 import java.util.Scanner; 3 4 public class Main { 5 int id = 0; 6 int withDrawmoney; 7 int depositmoney; 8 double balance = 0; 9 double annualInterestRate = 0; 10 // LocalDate dateCreated = LocalDate.parse("2020-7-31"); 11 Main(){ 12 } 13 Main(int id, double balance){ 14 this.id = id; 15 this.balance = balance; 16 } 17 private LocalDate dateCreated = LocalDate.of(2020, 7, 31); 18 public LocalDate getDateCreated() { 19 return dateCreated; 20 } 21 double getMonthlyInterestRate(double annualInterestRate ) { 22 23 double monthlyInterestRate = annualInterestRate / 1200; 24 return balance * monthlyInterestRate ; 25 26 } 27 void withDraw(int withDrawmoney) { 28 if(withDrawmoney > balance || withDrawmoney < 0) 29 System.out.println("WithDraw Amount Wrong"); 30 else 31 balance -= withDrawmoney; 32 33 } 34 35 void deposit(int depositmoney) { 36 37 if(depositmoney > 20000 || depositmoney < 0) 38 System.out.println("Deposit Amount Wrong"); 39 else 40 balance += depositmoney; 41 } 42 43 public static void main(String[] args) { 44 45 Scanner in = new Scanner(System.in); 46 Main account = new Main(); 47 48 account.id = in.nextInt(); 49 account.balance = in.nextDouble(); 50 account.annualInterestRate = in.nextDouble(); 51 account.withDrawmoney = in.nextInt(); 52 account.depositmoney = in.nextInt(); 53 54 account.withDraw(account.withDrawmoney); 55 account.deposit(account.depositmoney); 56 57 58 System.out.printf("The Account'balance:%.2f\n", (account.balance)); 59 System.out.printf("The Monthly interest:%.2f\n", (account.getMonthlyInterestRate(account.annualInterestRate))); 60 System.out.print("The Account'dateCreated:" + account.getDateCreated()); 61 62 63 64 } 65 66 }
(5)题集三——7-2 定义日期类:
设计: 创建一个类,属性(数据域)有年月日,方法有“判断闰年”、“判断月份”、“计算下一天并输出” --> 在主函数里定义并初始化一个该类的对象 -->
在主函数里调用对象的方法输出结果。
分析: 这道题的难度与上一题“7-1 创建账户类Account”的差不多,逻辑与“题集二——7-4 求下一天”的差不多,甚至可以引用相关代码。
意图在让我们巩固类和对象的知识,在此就不多讲了。
(6)题集三——7-3 一元多项式求导(类设计):
设计: 1.对输入的字符串去空格 --> 1.分五种情况判断:常数、幂函数、只含系数的单项式、即含系数又含指数的单项式、多项式 --> 根据判断的结果求导具体可见下面的代码。
分析: 这道题是这三次题集中最难的一道题,也学到了很多关于字符串的东西,并简单掌握了一个复杂而强大的东西——“正则表达式”。
这道题我没有拿到满分,除了有一个错误已知——求导得到的系数和指数可能过大,要用BigInteger类来储存系数和指数外 其它的错误暂时还不知道错在哪里,
希望我能解决。
主函数:
public static void main(String[] args) { // TODO 自动生成的方法存根 Scanner in = new Scanner(System.in); String input = in.nextLine().replace(" ", "");// 读取并去除空格 String output = ""; int type = judge(input); if(type == 5)//如果是多项式 output = result4(input); else if(type == 0)//如果都不符合即输入错误 System.out.println("Wrong Format"); else//如果是四种单项情况 output = Result(type, input); System.out.print(output); }
“常数、幂函数、只含系数的单项式、即含系数又含指数的单项式、多项式”的判断:
// 判断是否为常数 public static boolean constant(String input) { if (input.matches("(-|\\+)?\\d*")) return true; else return false; } // 判断是否为幂函数 public static boolean singleItem1(String input) { if (input.matches("(x\\^[\\+|-]?[1-9]\\d*)")) return true; else return false; } // 判断是否为只含系数单项式 public static boolean singleItem2(String input) { if (input.matches("([\\+|-]?[1-9]\\d*\\*x)|([\\+|-]?x)")) return true; else return false; } // 判断有指数和系数的单项式 public static boolean singleItem3(String input) { if (input.matches("([\\+|-]?[1-9]\\d*\\*x\\^[\\+|-]?[1-9]\\d*)|(([\\+|-]?x\\^[\\+|-]?[1-9]\\d*))")) return true; else return false; } // 判断多项式是否正确 public static boolean polynomial(String input) { if (input.matches( "(([\\+|-]?[1-9]\\d*\\*x\\^[\\+|-]?[1-9]\\d*)|(([\\+|-]?x\\^[\\+|-]?[1-9]\\d*))|
(x\\^[\\+|-]?[1-9]\\\\d*)|([\\+|-]?[1-9]\\d*\\*x)|([\\+|-]?x)|((-|\\+)?\\d*))([-|\\+](([\\+|-]?[1-9]\\d*\\*x\\^[\\+|-]?[1-9]\\d*)|
(([\\+|-]?x\\^[\\+|-]?[1-9]\\d*))|(x\\^[\\+|-]?[1-9]\\d*)|([\\+|-]?[1-9]\\d*\\*x)|([\\+|-]?x)|((-|\\+)?\\d*)))*")) return true; else return false; } // 判断类型 public static int judge(String input) { int a = 0; if (constant(input))// 指数 a = 1; else if (singleItem1(input))// 幂函数 a = 2; else if (singleItem2(input))// 只含系数的单项式 a = 3; else if (singleItem3(input))// 含系数和指数项 a = 4; else if (polynomial(input))// 多项式 a = 5; return a; }
在寻找系数和指数的时候我用了以下函数:
indexOf(ch):返回字符串中出现的第一个ch的下标。如果没有匹配的,返回-1;
charAt(index) :返回字符串中指定位置的字符;
substring(beginIndex):返回该字符串的子串,从指定位置beginIndex的字符开始到字符串的结尾;
substring(beginIndex,endIndex):返回该字符串的子串,从指定位置beginIndex的字符开始到下标为endIndex-1的字符。
Integer.parseInt(IntString):将数值型字符串转换为数值。
一开始我的思路是用indexOf(ch)直接找到‘ * ’这个字符的下标,再用substring(beginIndex,endIndex)将它之前的字符提取并用Integer.parseInt(IntString)将之转换为数值。但是后来发现一个bug就是像‘x’和‘-x’的情况,这的特殊情况不仅找不到‘ * ’而且提取时会出现错误就是“访问下标超出”。后来进行改进:寻找‘x’的下标然后判断它前面的字符是否为‘ * ’再进行提取,特殊情况单独考虑。代码如下:
// 获取输入表达式的系数 public static int inputCoefficient(String input) { int tempt = input.indexOf('x'); int a = 0; if (input.charAt(tempt - 1) == '*') a = Integer.parseInt(input.substring(0, tempt - 1)); else if(tempt == 0) a = 1; else if(input.charAt(tempt - 1) == '-') a = -1; return a; } // 获取输入表达式的指数 public static int inputExponent(String input) { int tempt = input.indexOf('^'); return Integer.parseInt(input.substring(tempt + 1)); } // 计算输出表达式的系数 public static int outputCoefficient(String input) { return inputCoefficient(input) * inputExponent(input); } // 计算输出表达式的指数 public static int outputExponent(String input) { if(inputExponent(input) == 1) return 1;//如果指数是1则返回1 else return inputExponent(input) - 1; }
拆分多项式:在一开始拆分多项式的时候挺棘手的不知如何去写,尤其是各个式子之间的加减号,后来通过查资料,正则表达式和Match类的结合使用让我成功把它们正确拆开。
// 拆分多项式 public static String[] separate(String input) { String[] stringArray = new String[30]; Matcher m = Pattern.compile( "([\\+|-]?[1-9]\\d*\\*x\\^[\\+|-]?[1-9]\\d*)|(([\\+|-]?x\\^[\\+|-]?[1-9]\\d*))
|(x\\^[\\+|-]?[1-9]\\d*)|([\\+|-]?[1-9]\\d*\\*x)|([\\+|-]?x)|((-|\\+)?\\d*)") .matcher(input); for (int i = 0; m.find(); i++) { stringArray[i] = m.group(); } return stringArray; }
各种情况求导:
// 计算幂函数的导数 public static String result1(String input) { int tempt = input.indexOf('^'); if (Integer.parseInt(input.substring(tempt + 1)) == 1) return "x";// 如果输入为x^1,返回x if (Integer.parseInt(input.substring(tempt + 1)) == -1) return "-x";// 如果输入为x^1,返回x String a = Integer.parseInt(input.substring(tempt + 1)) + "x^" + (Integer.parseInt(input.substring(tempt + 1)) - 1); return a; } // 计算只含系数单项式的导数 public static String result2(String input) { String a = inputCoefficient(input)+""; return a; } // 计算有系数和指数单项式的求导结果 public static String result3(String input) { String a; if(outputCoefficient(input) == 1 && outputExponent(input) == 1) a = "x";//指数系数都为1 else if (outputCoefficient(input) == 1) a = "x^" + outputExponent(input);//系数为1 else if (outputCoefficient(input) == -1) a = "-x^" + outputExponent(input);//系数为-1 else if (outputExponent(input) == 1) a = outputCoefficient(input) + "*x";//指数为1 else a = outputCoefficient(input) + "*x^" + outputExponent(input); return a; } // 计算多项式的求导结果 public static String result4(String input) { String[] stringArray = new String[30-1]; stringArray = separate(input);// 将字符串拆分并存入字符串数组 int type = 0; String output; //先将第一个项传进字符串 if(Result(judge(stringArray[0]), stringArray[0]).equals("0")) output = ""; else output = Result(judge(stringArray[0]), stringArray[0]); //将后续项项传进字符串 for (int i = 1; stringArray[i] != null; i++) { //判断类型 type = judge(stringArray[i]); stringArray[i] = stringArray[i].replaceAll("\\s+", "-"); //跳过空项 if (stringArray[i].equals("-")) continue; //跳过0项 else if (Result(type, stringArray[i]).equals("0")) continue; //相加 else if (output == "") output += Result(type, stringArray[i]); else if (Result(type, stringArray[i]).charAt(0) =='-') output += Result(type, stringArray[i]); else output += "+" + Result(type, stringArray[i]); } return output; } }
三、采坑心得:
在写这三个题集的过程中难免掉过很多坑,接了下来我将分享几个另我记忆最深刻坑点:
1. 在 题集一——7-8 判断三角形类型 中,对double类型的三角形三边数据进行判断的时候不能用“ == ”这样的运算符,因为浮点型的值是不精确的,不能直接进行比较,需采用两数相减小于一个很小的值判断,比如:a-b<0.00001这样即可以认为a和b相等。
2.“访问下标超出”是我在写代码过程中经常会遇到的问题,会出现像这样的报错“java.lang.StringIndexOutOfBoundsException:”,所以我在这提醒大家要注重这方面的代码哦。
3.“next()”和“nextLine()”的用法,读取方式有所不同,两者的混用会导致错误。
4.在 题集三——7-3 一元多项式求导 中,我一开始只进行了一个常数判断,然后去pta过测试点都过了三个得了15分,然后后面把代码写的差不多了去测试,原先对的两个测试点一直说我有格式错误,我一开始还以为我在输出结果那里少或者多打了空格之类的,直到后来我把“println”改成“print”就过了,我就明白了什么,有时候的输出格式也是很重要的,如果你要过测试点的话。
5.静态和非静态之间的关系,一开始在类中写方法时,有时会报错,说静态方法不能调用实例方法,对实例数据域进行修改。
四、改进建议:将功能细分给方法,这样可以让代码简洁,增强可读性。
五、总结:
我将分享我所学到的新知识:
1.next() 方法读取以空白字符(' '、'\t'、'\r'、'\f'、'\n')结束的字符串;nextLine()方法读取一整行文本,以按下回车键为结束标志的字符串。
2.正则表达式,我是从哔哩哔哩上学习的:https://www.bilibili.com/video/BV1da4y1p7iZ?t=609。
3.将数组传递给方法:对数组类型参数,参数值是数组的引用,传递给方法的是这个引用(传递共享信息)。
4.修饰符:
static修饰符:静态变量被类中的所有对象所共享。静态方法不能访问类中的实例成员。
public修饰符:在类、方法和数据域前使用public可见性修饰符,表示它们可以被任何其它的类访问。
private修饰符:修饰符限定方法和数据域只能在它自己的类中被访问。
如果没有使用可见性修饰符,那么默认类、方法和数据域是可以被同一个包中的任何一个类访问的,这称作包私有或包访问。
5.数据域封装:将数据域设为私有可以保护数据,在一开始接触类的时候我就有一个疑问——明明在主函数里“对象的名称 . 数据域名称”
就可以调用数据,为什么还要用set和get的方法呢。在看完书上有关知识点后明白了:●首先。 数据可能被篡改。●其次,它使得类难以维护,
同时容易出现错误。为了避免对数据域的直接修改,应该使用private修饰符将数据域声明为私有的,这称为数据域封装。所以从现在开始,
除非特别的原因而另外指定,否则所有数据域--私有、构造方法和方法--公共。

浙公网安备 33010602011771号