Java初步学习记录(pta作业集总结01-03)
Java初步学习记录
一、前言
以下开始针对前三次Java pta作业集做出以下总结(任务罢了( ̄▽ ̄)):
这三次次所做的pta作业整体而言有一定难度,并且每次均做出了时间的限制,短短几周内就通过学从的经验把Java的基本语法基本掌握不得不说有一定难度。01题目集总共题量比较大,直接给初学者的我一个狠狠的下马威,不过题目难度还可以,不需要花费过多的时间,其中几道基础题目用于学习基本的输入输出条件循环语法,而后几道题目针对于Java中字符串的处理,稍微麻烦一点,但出的比较浅显,而gps那道题目着实让人没有做的欲望,题目太长了,看都不想看。02题目集虽然题目多,但总体跨度不大,都是Java基本语法的训练,还可以。03题目集跨度比较大,需要专门去学习Java中的一大特色封装,并且对于类和对象需要有一定的理解,而且最后一题的测试点比较全面,故而对应的代码需要不断的完善,很花费时间。
二、设计与分析
1、题目集01
大多都是比较简单的题目,不过其中字符串和语言大有不同,需要重点学习,比较有代表性的就是7-8题了。
7-8 从一个字符串中移除包含在另一个字符串中的字符
作者 殷伟凤 单位 浙江传媒学院
题目描述:从一个字符串中移除包含在另一个字符串中的字符。输入两行字符串,将第一行字符串中包括第二行字符串中的所有字母去除。输出去除后保留的字符串。
输入格式:
第一行输入一个字符串
第二行输入一个字符串
输出格式:
输出移除后的字符串
输入样例:
在这里给出一组输入。例如:
abcdefghijklmnopqrstuvwxyz
secret
输出样例:
在这里给出相应的输出。例如:
abdfghijklmnopquvwxyz
代码长度限制 16 KB
时间限制 400 ms
内存限制 64 MB
Steps:初次学习Java,对于其中的字符串对象不太熟悉,尤其是字符串类是无法改变的这一特性,与c语言大相径庭。故我先去查如何改变原字符串的类,而后使用StringBuffer做完了该题。
Ideas:定义一个StringBuffer类str获得第一个输入,而后再定义一个String类s1获得对比的字符串。而后再通过循环对str中有无s1字符串中单个字符(通过substring方法截取至字符串compare中)进行比对(indexOf方法),而后用StringBuffer中deleteCharAt方法删除指定下标的字符即可。
while(i < length){//length为字符串s1的长度
compare = s1.substring(i,i+1);
index = str.indexOf(compare);
while(index != -1){//直到str中不再含有指定字符
str.deleteCharAt(index);
index = str.indexOf(compare);
}
i++;
}
由于难度不大所有测试点一遍就能够过了,不过该题还能先转化我字符数组(toCharArray方法),而对于熟悉的字符数组进行查改操作比较简单,此处不过多赘述。
以下为源代码
import java.util.*;
public class Main {
public static void main(String[] args) {
String s,s1;
int i = 0,index = 0,length;
String compare;
Scanner input = new Scanner(System.in);
s = input.nextLine();
s1 = input.nextLine();
StringBuffer str=new StringBuffer("");
str.append(s);
length = s1.length();
while(i < length){
compare = s1.substring(i,i+1);
index = str.indexOf(compare);
while(index != -1){
str.deleteCharAt(index);
index = str.indexOf(compare);
}
i++;
}
System.out.print(str);
}
}
2、题目集02
相较于第一次难度有了不小的提升,不过对个人而言写起来比第一次顺手不少,除了7-8浪费了一些时间,其他的都比较快就解决了,或许现在开始是有了一点基础的原因( ╯▽╰),此处7-7由于开始一直想通过StringBuffer类写也花费了不少时间。
7-7 二进制数值提取
作者 蔡轲 单位 南昌航空大学
题目描述:在一个字符串中提取出其中的二进制数值序列。
输入格式:
一个由0、1构成的序列,以-1为结束符,非0、1字符视为正常输入,但忽略不计,未包含结束符的序列视为非法输入。例如:abc00aj014421-1
输出格式:
将输入的序列去掉非0、1字符以及结尾符的数据内容,
注:结束符-1之后的0\1字符忽略不计。
例如:00011。
输入样例:
在这里给出一组输入。例如:
abc00aj014421-1
输出样例:
在这里给出相应的输出。例如:
00011
输入样例1:
在这里给出一组输入。例如:
a0571-1k001y
输出样例1:
在这里给出相应的输出。例如:
01
代码长度限制 16 KB
时间限制 400 ms
内存限制 64 MB
Steps:由于对于删改的惯性思维,开始比较倾向于使用StringBuffer类进行删改操作,意图通过对于“0”,“1”,“-1”字符串的查找来逐步消除“0”,“1”,字符,但麻烦点在于“-1”字符串中同样含有“1”字符串,而且其中对于“-1”不在末尾的情况会报错,因此改变思路,通过对原字符串中的字符提取出来逐步检查,符合要求的字符便加到新字符串s1中,最后输出s1即可。对于修改前的代码我会放到踩坑心得中讲解。
Ideas:定义一个String类s(接收输入)和s1(输出结果)。利用循环获得s中的每一个字符(charAt方法),不过每次需要获得两个字符,专门用于判断“-1”字符串,字符a获得第i位,字符b获得第i+1位,而后判断是否位上述字符中的一个即可,条件为真则将该字符a加到s1中,最后输出s1。
while(i < s.length()-1){
a = s.charAt(i);
b = s.charAt(i+1);
if(a == '0' || a == '1'){
s1 += a;
}else if(a == '-' && b == '1'){
flag = 1;//判断是否有结束符从而调整输出
break;
}
i++;
}
将思路更改后代码简单很多,而且比较容易看懂,写起来比较顺。测试点也是一遍就能过。所以在有时候卡题目时,适当更换思路可能有奇效。
以下为源代码
import java.util.*;
public class Main {
public static void main(String[] args) {
String s,s1 = "";
int i = 0,flag = 0;char a,b;
Scanner input=new Scanner(System.in);
s=input.nextLine();
while(i < s.length() - 1){
a = s.charAt(i);
b = s.charAt(i + 1);
if(a == '0' || a == '1'){
s1 += a;
}else if(a == '-' && b == '1'){
flag = 1;
break;
}
i++;
}
if(flag == 1){
System.out.print(s1);
}else{
System.out.print("Wrong Format");
}
}
}
3、题目集03
这次的题目集03难度上升了一个大档次,对于Java本身的特色封装做了很大的要求,而且需要完全理解类的定义和使用,以及如何通过需求去定义对象的个体和功能。在此之前,我对于Java中的类的概念其实非常模糊,所以做的过程比较折磨。刚开始就连第一题都弄了比较久,一直在查百度。不过通过去b站看了Java关于类和对象的网课后,一切都豁然开朗了,譬如getter以及setter的实际意义是什么,以及类的一般定义方法和具体实现,了解这些后写起来就顺手很多了,网课的链接放在下面,需要的可以看看。
https://www.bilibili.com/video/BV17F411T7Ao/?spm_id_from=333.337.search-card.all.click&vd_source=cab4352f0bd29a40ec8786615f500d5d
由于前两道题目在了解类和对象的定义后比较简单,此处仅讲解7-3和7-4两道比较麻烦的题目。
7-3 定义日期类
作者 段喜龙 单位 南昌航空大学
题目描述:定义一个类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
代码长度限制 16 KB
时间限制 400 ms
内存限制 64 MB
Steps:既然是需要定义类和对象的题目,自然需要先把类的关系和功能弄清楚。而类图在题目中已经给出。通过类图我们可以清晰明了的知道所定义的类中该包含哪些要素,以及彼此之间的关系。此外,对于其中的setter和getter方法以及无参和有参构造法能够通过idea中的快捷键快速生成,按住写好类中的个体后按住fn+alt+insert键
这样一个比较完整的类的基本定义已经完成,后续就是对于Date类的功能进行分析了并创建即可完成。
Ideas:首先是isLeapYear方法的创建,功能为分析一个年份是否为闰年,if条件判断即可,能被400整除或者呗4整除不被100整除的即为闰年。
public boolean isLeapYear(int year){
if(year % 400 == 0){
return true;
}else if(year % 4 == 0&&year % 100 != 0){
return true;
}else{
return false;
}
}
而后分析checkInput方法,对于类中的年月份检查是否符合题目规范,年月份没什么好说的,日份通过类中定义的mon_maxnum判断即可,注意闰年的二月份总天数需要+1。
public boolean checkInputValidity(){
if(isLeapYear(year)){//闰年二月份总天数加1
mon_maxnum[2]++;
}
if(year < 1900 || year > 2000){
return false;
}else{
if(month < 1||month > 12){
return false;
}else{
if(day>mon_maxnum[month] || day < 1){
return false;
}else{
return true;
}
}
}
}
最后便是获得下一天的方法了,判断day+1后是否会超过对于月份总天数即可,条件为真月份+1并且让day=1,再判断月份+1后是否大于12,条件为真month=1,年份+1。
public void getNextDate(){
if(day+1 > mon_maxnum[month]){
day = 1;
month++;
}else{
day++;
}
if(month > 12){
month = 1;
year++;
}
}
由于基本方法都放上去了,源代码就不贴了,以免过长o( ̄ヘ ̄o#),接下来是最为繁琐的7-4。
7-4 日期类设计
作者 段喜龙 单位 南昌航空大学
题目描述:参考题目3和日期相关的程序,设计一个类DateUtil,该类有三个私有属性year、month、day(均为整型数),其中,year∈[1820,2020] ,month∈[1,12] ,day∈[1,31] , 除了创建该类的构造方法、属性的getter及setter方法外,需要编写如下方法:
public boolean checkInputValidity();//检测输入的年、月、日是否合法
public boolean isLeapYear(int year);//判断year是否为闰年
public DateUtil getNextNDays(int n);//取得year-month-day的下n天日期
public DateUtil getPreviousNDays(int n);//取得year-month-day的前n天日期
public boolean compareDates(DateUtil date);//比较当前日期与date的大小(先后)
public boolean equalTwoDates(DateUtil date);//判断两个日期是否相等
public int getDaysofDates(DateUtil date);//求当前日期与date之间相差的天数
public String showDate();//以“year-month-day”格式返回日期值
应用程序共测试三个功能:
- 求下n天
- 求前n天
- 求两个日期相差的天数
注意:严禁使用Java中提供的任何与日期相关的类与方法,并提交完整源码,包括主类及方法(已提供,不需修改)
(主方法代码比较长,我们这里不做讲解,主要研究类方法如何构建)
输入格式:
有三种输入方式(以输入的第一个数字划分[1,3]):
- 1 year month day n //测试输入日期的下n天
- 2 year month day n //测试输入日期的前n天
- 3 year1 month1 day1 year2 month2 day2 //测试两个日期之间相差的天数
输出格式:
- 当输入有误时,输出格式如下:
Wrong Format
- 当第一个数字为1且输入均有效,输出格式如下:
year1-month1-day1 next n days is:year2-month2-day2
- 当第一个数字为2且输入均有效,输出格式如下:
year1-month1-day1 previous n days is:year2-month2-day2
- 当第一个数字为3且输入均有效,输出格式如下:
The days between year1-month1-day1 and year2-month2-day2 are:值
输入样例1:
在这里给出一组输入。例如:
3 2014 2 14 2020 6 14
输出样例1:
在这里给出相应的输出。例如:
The days between 2014-2-14 and 2020-6-14 are:2312
输入样例2:
在这里给出一组输入。例如:
2 1834 2 17 7821
输出样例2:
在这里给出相应的输出。例如:
1834-2-17 previous 7821 days is:1812-9-19
输入样例3:
在这里给出一组输入。例如:
1 1999 3 28 6543
输出样例3:
在这里给出相应的输出。例如:
1999-3-28 next 6543 days is:2017-2-24
输入样例4:
在这里给出一组输入。例如:
0 2000 5 12 30
输出样例4:
在这里给出相应的输出。例如:
Wrong Format
代码长度限制 12 KB
时间限制 10000 ms
内存限制 64 MB
Steps:和先前Date类的创建方法类似,先构思好对于的类图,如下所示
而后再逐一将其中的方法实现即可。
Ideas:从下n天方法开始,有了先前求一天的方法,其实n天可以参考先前写过的下一天方法,让其循环n次即可。不过此处我想到了其他的算法从时间效率上会更高一点,由于n可能很大,所以需要考虑n>365后年份+1的情况,也需要考虑每个月份最大总天数。首先,如果通过n对减去年总天数,那么我们自然就需要看下一年是闰年或者平年来减去不同的年天数,个人感觉麻烦就放弃了。而后我便考虑按月份计算的方法,比如3月10日的后25天,易知10+25>31(3月份总天数),故月份+1。我们day的计算方法就是10+25-31(3月份的总天数),故结果为4月4日。然后再扩展到n>一个月的总月数的情况,3月10日的后100天,计算方法同理,10+100>31故月份+1.....因而我们得出循环的条件为day+n>mon_maxnum[month],循环内部为day+n-=mon_maxnum[month]。至此,该方法基本完成,代码如下。
public DateUtil getNextNDays(int n){
//day+=n;//去掉”//“后为原始代码
while(n > mon_maxnum[month]){
isLeapYear(year);
n -= mon_maxnum[month];
month++;
if(month > 12){
month = 1;
year++;
}
}
//因为测试点有一个数据n恰好为int类型的最大值,不能对n进行相加(会爆int),所以改成先n循环后再把day加上去。
day += n;
if(day > mon_maxnum[month]){
day -= mon_maxnum[month];
month++;
if(month > 12){
month = 1;
year++;
}
}
return this;
}
由求下n天的方法,我们很容易退出前n天的方法,将day+n改为day-n,循环条件改为day<=0即可,剩下的原理相同,代码如下
public DateUtil getPreviousNDays(int n){
day -= n;
while(day <= 0){
isLeapYear(year);
day += mon_maxnum[month-1];
month--;
if(month < 1){
month = 13;
year--;
}
}
return this;
}
比较日期大小的方法直接if-else级联语句即可完成,代码如下
public boolean compareDates(DateUtil date){
if(year > date.year){
return true;
}else if(year == date.year){
if(month > date.month){
return true;
}else if(month == date.month){
if(day > date.day){
return true;
}
}
}
return false;
}
接下来为比较日期相同方法,同样能直接判断即可。
public boolean equalTwoDates(DateUtil date){
if(this.year == date.year && this.month == date.month && this.day == date.day){
return true;
}
return false;
}
注意: 接下来就是整道题比较巧妙的一点了。当一个类中方法较多时,可以思考下这些方法之间是否有什么联系,例如先前的判断闰年方法,在其它方法中同样多次用到。而在这个方法中,我们需要计算两个日期的差值,那么开始,肯定需要用到前面的compareDates 方法,不仅如此,既然我们前面写好了求下n天的方法,那么我们是否能通过让其中较小的日期一直通过getNextNDays 方法向下走一天,而当两个日期相等时 即equalTwoDates方法返回值为真时,退出循环。这样便完美的利用了前面的方法,并且能快速简便的将getDaysofDates 方法写完,代码如下
public int getDaysofDates(DateUtil date){
int n = 0;
if(equalTwoDates(date)){
return 0;
}else{
if(compareDates(date)){
while(true){
date = date.getNextNDays(1);
n++;
if(equalTwoDates(date)){
break;
}
}
}else{
while(true){
date = date.getPreviousNDays(1);
n++;
if(equalTwoDates(date)){
break;
}
}
}
}
return n;
}
最后一个String showDate方法是最为简单的,按照输出格式定义一个String类将各个体链接起来即可,代码如下
public String showDate(){
String d=year+"-"+month+"-"+day;
return d;
}
上面已经将题目所需的大部分方法源代码给出,此处便不在贴所有源码 φ(-ω-*)。
三、踩坑心得
题目集02
7-7二进制数值提取
修改思路前,按照StringBuffer类删改的代码如下
import java.util.*;
public class Main {
public static void main(String[] args) {
StringBuffer s1,s2;String s;
int index=0,index1=0,length=-1,compare=0;
Scanner input=new Scanner(System.in);
s=new String();
s1=new StringBuffer("");
s2=new StringBuffer("");
s=input.nextLine();
s1.append(s);//将s字符串加至s1
length=s1.indexOf("-1");//循环前先获得一次字符串中目标字符的位置
index=s1.indexOf("0");
index1=s1.indexOf("1");
while(compare<length){//当匹配的"0"或者"1"字符的位置大于“-1”时退出循环
length=s1.indexOf("-1");
index=s1.indexOf("0");
index1=s1.indexOf("1");
//本质问题出现在循环判断条件上
if(length==0){
break;
}
if(index==-1){
index=length;
}else if(index1==-1){
index1=length;
}
if(index<index1){//判断先加“0”还是“1”字符串
s2.append("0");
s1.delete(0,index+1);
}else{
s2.append("1");
s1.delete(0,index1+1);
}
}
if(length==-1){
System.out.print("Wrong Format");
}else{
System.out.print(s2);
}
}
public static int min(int a,int b){
if(a>b){
return b;
}else{
return a;
}
}
}
其实当时就错了一两种情况,但始终难以找出问题,如以下输入,运行后的输出就不符合要求。因此被迫更改了思路,改用字符判断的方式解决。
题目集02
7-8判断三角形类型
这题当时没一次过的问题就是栽在了等腰直角三角形上,一直将等腰直角三角形判断成等腰三角形导致有几个点出错,如下所示
之后试了直角三角形的测试,同样出现问题后,我便能肯定是直角的判定方式出了问题,原先的判定条件如下
if(b*b+c*c==a*a){
return true;
}else{
return false;
}
突然发现浮点数类型就算二者相乘也无法和整数2符合相等的条件,故判断条件永远为假,自然便输出了等腰三角形的结果。
于是将条件稍作修改,既然无法相等,但二者的值是非常接近的,只要二者的差值小于某个很小的常数,那么我就能近似认为两者相等,修改后如下
if(Math.abs(b*b+c*c-a*a)<0.01){
return true;
}else{
return false;
}
问题成功解决,同时学到浮点数之间计算的判断不能单纯的使用“==”运算符,需要使用其他的方法。
四、改进建议
其实大部分代码由于当初c语言的习惯没改,所以写的比较不规范,命名有些地方也有不少的问题,不过出去写法不规范的问题,绝大部分代码的逻辑或者算法基本没什么可改的了,这里针对7-4题的求n天介绍下另一种比较耗时间但看起来更为美观的将getNextDate方法循环n次的方法
while(n > 0){
this = this.getNextDate;
n--;
}
return this;
这种方法虽然比较耗时间,但并不存在n超过限制的可能性,继承性和泛用性也比较好。
五、总结
不得不承认,虽然两个星期内写完这三次的题目集压力比较大(毕竟每天的课程非常之多╮(╯▽╰)╭),但是这一轮下来收获也是比较多的,从0基础到现在基本了解对象和类,Java的进展很快。学会了Java前期的基本语法,不过对于Java中的字符串类的使用仍然学的不精,需要继续深入学习。
此外,对于老师完全放养式的方法,个人认为有利有弊吧,毕竟有的时候碰到一些看不懂的bug是真的要浪费好多时间嘞(*  ̄︿ ̄),当然具体的一些改进建议个人认为没什么可提的,大部分都还行,就是觉得作业其实可以稍微减少a litteX﹏X。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!