OOP课程题目集第一次总结
前言
本次总结针对于pta上第一阶段的三次题目集。
第一次题目集主要训练Java的基础语法,如if语句、for、while循环语句以及一些常见的字符串处理,题量较多,但大多简单;
第二次题目集主要训练逻辑思维,对七种基本数据类型有正确的了解和使用,进行类、构造方法等的使用, 题量一般,比较简单;
第三次作业主要训练类、构造方法等的使用,在主函数中实例化类并正确地调用相应的方法和属性解决问题,题量少,难度一般;
本次总结将对部分难题和所遇见的问题进行一次分析和总结。
总结来自:南昌航空大学-22201335
设计与分析
下面对三次题目集的部分题目进行设计与分析。
1-7 有重复的数据:
题面较少,先列出主要任务。
- 读入数据,检查是否有重复的数据;
观察题目,数据数量是10的五次方所以最多写一重循环,可以使用Arrays.sort()方法使数据更好处理,这样计算是否有相同元素,只需要比较排序后该元素前后是否相等就可以了。
完整代码如下
import java.util.Scanner;
public class Main{
public static void main(String []args){
Scanner scan = new Scanner(System.in);
int x = scan.nextInt();
int[] c = new int[x];
boolean t = true;
for(int i = 0;i<x;i++){
c[i] = scan.nextInt();
}
Arrays.sort(c);
for(int i = 1;i<x;i++){
if(c[i-1]==c[i]){
t = false;
}
}
if(t==true){
System.out.print("NO");
}else{
System.out.print("YES");
}
}
}
其实这样写运行时间很极限( ´◔︎ ‸◔︎`)。
相关复杂度解析
可以看出这个程序的平均复杂度还是高了以及少了足够的注释,需要一点小改进。
1-10 GPS数据处理:
先读题并简单分析,列出任务:
- 找出
$GPRMC
语句,未找到则找下一条语句,数据以“END”结尾; - 计算校验和,找出其中校验正确;
- 字段2的状态为 'A';
- 计算出时间,换算成北京时间;
- 一次数据中会包含多条
$GPRMC
语句,以最后一条语句得到的北京时间作为结果输出;
对其任务分别写出解决方案,大致思路如下:
1.用while循环解决数据是否结尾,用str存放语句。
String str = scan.nextLine();
while(!str.equals("END")){
2.将split方法将语句切割,用if找出$GPRMC
语句。
String[] s = str.split(",");
if(!s[0].equals(right))
{
str=in.nextLine();
continue;
}
3. 用for循环和charAt方法算出异或的校验值。
int i = 2;
int result;
char ch;
ch=str.charAt(1);
for(result=str.charAt(1);ch!='*';ch=str.charAt(i))
{
ch=str.charAt(i);
result^=(int)ch;
i++;
}
result%=65536;
4.比较校验值是否相等和字段二状态是否正确,若都为真则将存有该语句时间的字符串存入now中,以便后续打印。
char state = s[2].charAt(0);//定位状态
num=num.toLowerCase();
if(num.equals(Integer.toHexString(result))&&state=='A') {
now = s[1];
}
5.最后用substring分割字符串来输出时间。
String hh=now.substring(0, 2);
String mm =now.substring(2, 4);
String ss =now.substring(4, 6);
int hour = Integer.parseInt(hh);
hour=(hour+8)%24;
if(hour<10) {
System.out.print(0);//补0
}
System.out.println(hour+":"+mm+":"+ss);
最后对程序进行细节补充得:
完整代码如下
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str = scan.nextLine();
String right ="$GPRMC";
String now = null;
while(!str.equals("END")) {
String[] s = str.split(",");
if(!s[0].equals(right))
{
str=scan.nextLine();
continue;
}
int i = 2;
int result;
char ch;
ch=str.charAt(1);
for(result=str.charAt(1);ch!='*';ch=str.charAt(i))
{
ch=str.charAt(i);
result^=(int)ch;
i++;
}
result%=65536;
String num = str.substring(i+1, i+3);
char state = s[2].charAt(0);//定位状态
num=num.toLowerCase();
if(num.equals(Integer.toHexString(result))&&state=='A') {
now = s[1];
}
str=scan.nextLine();
}
if(now==null){
System.exit(0);
}
String hh=now.substring(0, 2);
String mm =now.substring(2, 4);
String ss =now.substring(4, 6);
int hour = Integer.parseInt(hh);
hour=(hour+8)%24;
if(hour<10) {
System.out.print(0);//补0
}
System.out.println(hour+":"+mm+":"+ss);
}
}
相关复杂度分析:
缺点:该代码的平均复杂度高以及少了足够的注释,同时没写方法,都写在了主函数里,不易维护和复用以及分支嵌套的层数有点多。
3-3 定义日期类
先读题并简单分析,列出任务:
- 定义一个Date类;
- 在Date类中写好相应的属性和get,set方法和构造方法;
- 对数据进行合理判断和接受;
- 数据非法及输入日期不存在时,输出“Date Format is Wrong”;
- 输入日期合法时,输出下一天;
对其任务逐个实现,大致思路如下:
先对类进行设计再写主函数,
类的一些主要方法,如下:
1.对润年进行判断,返回boolean值
public boolean isLeapYear(int year){
if( (year%4 == 0 && year%100 != 0) || year%400==0 ) {
this.mon_maxnum[2] = 29;
return true;
}else{
return false;
}
}
2.判断输入日期是否正确,返回boolean值
public boolean checkInputValidity(){
if(this.year<1900||this.year>2000||this.month<1||this.month>12||this.day<1||this.day>mon_maxnum[this.month]){
return false;
}else{
return true;
}
}
3.计算下一天并输出
public void getNextDate(){
day++;
if(day>mon_maxnum[month]){
day -= mon_maxnum[month];
month++;
}
if(month>12){
month = 1;
year++;
}
System.out.print("Next day is:"+year+"-"+month+"-"+day);
}
主函数中创建对象调用对应方法,并对数据进行合理判断
Date date = new Date(year, month, day);
date.isLeapYear(year);
if(date.checkInputValidity()){
date.getNextDate();
}else{
System.out.print("Date Format is Wrong");
}
分析结束。
完整代码如下:
import java.util.Scanner;
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};
public Date() {
}
public 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 ) {
this.mon_maxnum[2] = 29;
return true;
}else{
return false;
}
}
public boolean checkInputValidity(){
if(this.year<1900||this.year>2000||this.month<1||this.month>12||this.day<1||this.day>mon_maxnum[this.month]){
return false;
}else{
return true;
}
}
public void getNextDate(){
day++;
if(day>mon_maxnum[month]){
day -= mon_maxnum[month];
month++;
}
if(month>12){
month = 1;
year++;
}
System.out.print("Next day is:"+year+"-"+month+"-"+day);
}
}
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int year = scan.nextInt();
int month = scan.nextInt();
int day = scan.nextInt();
Date date = new Date(year, month, day);
date.isLeapYear(year);
if(date.checkInputValidity()){
date.getNextDate();
}else{
System.out.print("Date Format is Wrong");
}
}
}
相关复杂度分析:
整体还可以依旧是不爱写注释,以后得改
相关类图:
3-4 日期类设计
本题目标明确按照题目编写以下方法:
- 设计一个类DateUtil
- 1.public boolean checkInputValidity(); //检测输入的年、月、日是否合法
- 2.public boolean isLeapYear(int year); //判断year是否为闰年
- 3.public DateUtil getNextNDays(int n); //取得year-month-day的下n天日期
- 4.public DateUtil getPreviousNDays(int n); //取得year-month-day的前n天日期
- 5.public boolean compareDates(DateUtil date);//比较当前日期与date的大小(先后)
- 6.public boolean equalTwoDates(DateUtil date);//判断两个日期是否相等
- 7.public int getDaysofDates(DateUtil date); //求当前日期与date之间相差的天数
- 8.public String showDate(); //格式返回日期值
本题只用写好类,先将类的属性,get,set方法和构造方法写出,然后对逐个实现所需方法
下面逐个分析每个方法
1.检测输入的年、月、日是否合法,返回boolean值
public boolean checkInputValidity(){
if(isLeapYear(year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
if(this.year<1820||this.year>2020||this.month<1||this.month>12||this.day<1||this.day>mon_maxnum[this.month]){
return false;
}else{
return true;
}
}
2.判断year是否为闰年,返回boolean值
public boolean isLeapYear(int year){
if( (year%4 == 0 && year%100 != 0) || year%400==0 ) {
return true;
}else{
return false;
}
}
3.取得year-month-day的下n天日期,返回对象
为防止传入的 day+=n 超过int所能存的最大值,使用递归分次对数据进行处理
public DateUtil getNextNDays(int n){
if(isLeapYear(year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
if(n>2000000000){//int最大值会越界
getNextNDays(2000000000);
n -= 2000000000;
}
day+=n;
while(day>mon_maxnum[month]){
day -= mon_maxnum[month];
month++;
if(month==13){
month -= 12;
year++;
if(isLeapYear(year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
}
}
return this;
}
4.取得year-month-day的前n天日期,返回对象
public DateUtil getPreviousNDays(int n){
int temp = day;
day-=n;
while(day<1){
if(isLeapYear(year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
month--;
if(month==0){
month += 12;
year--;
}
day += mon_maxnum[month];
}
return this;
}
5.比较当前日期与date的大小(先后),返回boolean值
public boolean compareDates(DateUtil date){
if(this.year>date.year||(this.year==date.year&&this.month>date.month)||(this.year==date.year&&this.month==date.month&&this.day>date.day)) {
return true;
}else{
return false;
}
}
6.判断两个日期是否相等,返回boolean值
public boolean equalTwoDates(DateUtil date){
if(this.year==date.year&&this.month==date.month&&this.day==date.day) {
return true;
}else{
return false;
}
}
7.求当前日期与date之间相差的天数,返回int型相差天数
//当时这个很多的地方有小问题,结果因为测试点太少了,混对了,现在回头才发现( ´◔︎ ‸◔︎`)//
public int getDaysofDates(DateUtil date){
int tempday = date.day-this.day;
if(this.month>date.month) {
for (int i = date.month; i < this.month; i++) {
if(isLeapYear(date.year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
tempday -= mon_maxnum[i];
}
}else {
for (int i = this.month; i < date.month; i++) {
if(isLeapYear(date.year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
tempday += mon_maxnum[i];
}
}
if(this.year > date.year) {
int tempyear = date.year;
while(this.year>tempyear){
tempyear++;
if(isLeapYear(tempyear)) {
tempday -= 366;
}else{
tempday-= 365;
}
}
}else{
int tempyear = this.year;
while(date.year>tempyear){
if(isLeapYear(tempyear)) {
tempday += 366;
}else{
tempday += 365;
}
tempyear++;
}
}
return tempday;
}
8.格式返回日期值,返回格式化后的字符串
public String showDate(){
return year+"-"+month+"-"+day;
}
主函数题目给好了,直接合并使用
完整代码如下:
import java.util.Scanner;
class DateUtil{
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};
public DateUtil() {
}
public DateUtil(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(isLeapYear(year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
if(this.year<1820||this.year>2020||this.month<1||this.month>12||this.day<1||this.day>mon_maxnum[this.month]){
return false;
}else{
return true;
}
}
public DateUtil getNextNDays(int n){
if(isLeapYear(year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
if(n>2100000000){//int最大值会越界
getNextNDays(2100000000);
n -= 2100000000;
}
day+=n;
while(day>mon_maxnum[month]){
day -= mon_maxnum[month];
month++;
if(month==13){
month -= 12;
year++;
if(isLeapYear(year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
}
}
return this;
}
public DateUtil getPreviousNDays(int n){
int temp = day;
day-=n;
while(day<1){
if(isLeapYear(year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
month--;
if(month==0){
month += 12;
year--;
}
day += mon_maxnum[month];
}
return this;
}
public boolean compareDates(DateUtil date){
if(this.year>date.year||(this.year==date.year&&this.month>date.month)||(this.year==date.year&&this.month==date.month&&this.day>date.day)) {
return true;
}else{
return false;
}
}
public boolean equalTwoDates(DateUtil date){
if(this.year==date.year&&this.month==date.month&&this.day==date.day) {
return true;
}else{
return false;
}
}
public int getDaysofDates(DateUtil date){
int tempday = date.day-this.day;
if(this.month>date.month) {
for (int i = date.month; i < this.month; i++) {
if(isLeapYear(date.year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
tempday -= mon_maxnum[i];
}
}else {
for (int i = this.month; i < date.month; i++) {
if(isLeapYear(date.year)==true){
mon_maxnum[2]=29;
}else{
mon_maxnum[2]=28;
}
tempday += mon_maxnum[i];
}
}
if(this.year > date.year) {
int tempyear = date.year;
while(this.year>tempyear){
tempyear++;
if(isLeapYear(tempyear)) {
tempday -= 366;
}else{
tempday-= 365;
}
}
}else{
int tempyear = this.year;
while(date.year>tempyear){
if(isLeapYear(tempyear)) {
tempday += 366;
}else{
tempday += 365;
}
tempyear++;
}
}
return tempday;
}
public String showDate(){
return year+"-"+month+"-"+day;
}
}
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int year = 0;
int month = 0;
int day = 0;
int choice = input.nextInt();
if (choice == 1) { // test getNextNDays method
int m = 0;
year = Integer.parseInt(input.next());
month = Integer.parseInt(input.next());
day = Integer.parseInt(input.next());
DateUtil date = new DateUtil(year, month, day);
if (!date.checkInputValidity()) {
System.out.println("Wrong Format");
System.exit(0);
}
m = input.nextInt();
if (m < 0) {
System.out.println("Wrong Format");
System.exit(0);
}
System.out.print(date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " next " + m + " days is:");
System.out.println(date.getNextNDays(m).showDate());
} else if (choice == 2) { // test getPreviousNDays method
int n = 0;
year = Integer.parseInt(input.next());
month = Integer.parseInt(input.next());
day = Integer.parseInt(input.next());
DateUtil date = new DateUtil(year, month, day);
if (!date.checkInputValidity()) {
System.out.println("Wrong Format");
System.exit(0);
}
n = input.nextInt();
if (n < 0) {
System.out.println("Wrong Format");
System.exit(0);
}
System.out.print(
date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " previous " + n + " days is:");
System.out.println(date.getPreviousNDays(n).showDate());
} else if (choice == 3) { //test getDaysofDates method
year = Integer.parseInt(input.next());
month = Integer.parseInt(input.next());
day = Integer.parseInt(input.next());
int anotherYear = Integer.parseInt(input.next());
int anotherMonth = Integer.parseInt(input.next());
int anotherDay = Integer.parseInt(input.next());
DateUtil fromDate = new DateUtil(year, month, day);
DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay);
if (fromDate.checkInputValidity() && toDate.checkInputValidity()) {
System.out.println("The days between " + fromDate.showDate() +
" and " + toDate.showDate() + " are:"
+ fromDate.getDaysofDates(toDate));
} else {
System.out.println("Wrong Format");
System.exit(0);
}
}
else{
System.out.println("Wrong Format");
System.exit(0);
}
}
}
相关复杂度分析:
可以看出除了注释少了以外,程序的最大复杂度很高,以及平均分支嵌套的层数有点多,会影响可读性。
类图展示:
采坑心得
在第二次题目集中大量使用浮点型数据,需要对其有一定的认识,
如float和double的有效精度范围,
float是单精度类型,精度是8位有效数字,double是双精度类型,精度是17位有效数字,
if(Math.abs(next-last)<=0.00001){
}
改进建议
后续编码过程中加强注释的合理使用,不怎么喜欢写方法,什么事都要主函数来干,复用性较差,不好阅读。¯\_(ツ)_/¯
总结
本次3个题目集作为Java入门的习题练习,主要为基本数据的运算,数字大小的排序,字符串的处理,日期的处理,对基本的逻辑能力有一定的考察,
对类有一个基本的了解,能够简单设计单个类,合理设计出相应的属性和方法。