题目集1~3的总结性Blog

发布时间 2023-10-07 17:51:23作者: slowfover

第一次博客

一、前言

前三次的pta题目难度逐渐增加,第一次pta还算比较简单,基本不涉及到多个类,但是到了第二次作业就开始涉及到多个类,难度也比第一次的增长了很多,第三次作业虽然题目量不多,但是题目很难,涉及到的类关系非常复杂。难度非常大,测试点很多。

前三次的pta题目主要涉及到的知识点有:

  • 输入输出流处理:

    根据输入格式要求,使用Scanner类或BufferedReader类等读取用户输入的数据,并根据输出格式要求使用System.out.print()或System.out.println()打印结果。

  • for循环可以与数组、集合等数据结构一起使用,用于遍历元素。比如:

    for(char a :number.toCharArray()){
    	if(a == '0' || a== '1'){
    		sequence += a;
    	}
    
  • 字符串处理:

    1.contains是一个用于判断字符串是否包含指定字符序列的方法。它属于String类的方法,可以用于任何字符串对象。

    boolean result = str.contains(substring);
    

    其中,str是需要被检查的字符串,substring是需要查找的字符序列。

    contains方法会返回一个boolean值,如果在str中找到了substring,则返回true;如果没有找到,则返回false。

  • 对象的封装:

    对象的封装(Encapsulation)是一种将对象的数据(属性)和操作(方法)封装在一起,通过使用访问修饰符(如private、public、protected)限制对对象的直接访问,从而控制对象的访问权限和数据的安全性的机制。

    如下面这个例子:

    public class Person {
        private String name;
        private int age;
        public String getName() {
            return name;
        }
        public int getAge() {
            return age;
        }
    }
    
  • Scanner类中的nextLine()方法以及String类中的split()方法。

    使用Scanner类中的nextLine()方法可以从控制台读取用户输入的一行字符串。将读取到的字符串存储在名为line的变量中供后续使用。

    使用String类中的split()方法可以将一个字符串按照指定的分隔符进行分割,返回一个由分割后的子串构成的字符串数组。

  • HashSet

    HashSet的常用方法包括:

    • add(element):向HashSet中添加指定的元素,如果元素已存在则不会重复添加。

    • remove(element):从HashSet中移除指定的元素。

    • contains(element):判断HashSet中是否包含指定的元素。

    • size():返回HashSet中元素的数量。

    • isEmpty():判断HashSet是否为空。

    • clear():清空HashSet中的所有元素。

  • 有参构造和无参构造

    有参构造函数和无参构造函数的区别:

    • 参数列表:有参构造函数在定义时需要指定参数列表,而无参构造函数不需要。
    • 初始化操作:有参构造函数可以根据传入的参数值执行更加灵活的初始化操作,而无参构造函数一般执行一些默认的初始化操作。
    • 调用方式:通过传入不同的参数来调用不同的有参构造函数,而无参构造函数可以直接通过new关键字调用。

    需要注意的是,如果一个类显式地定义了有参构造函数,而没有定义无参构造函数,那么在创建对象时如果想使用无参构造函数,需要自己显式地定义一个无参构造函数。

  • java.time包中的LocalDate

    在Java中,针对年、月、日等日期相关的操作和判断,可以使用java.time包中的LocalDate类和相关方法。以下是一些常用的日期判断方法的介绍:

    1. 判断闰年:
      • isLeapYear()方法:LocalDate类中的静态方法,用于判断指定年份是否是闰年。返回一个布尔值,true表示是闰年,false表示不是闰年。
    2. 判断两个日期的先后顺序:
      • isBefore()isAfter()方法:LocalDate类中的方法,用于判断一个日期是否在另一个日期之前或之后。返回一个布尔值,true表示是,在之前(或之后),false表示不在之前(或之后)。
    3. 判断日期的相等性:
      • isEqual()方法:LocalDate类中的方法,用于判断两个日期是否相等。返回一个布尔值,true表示相等,false表示不相等。
    4. 判断是否是有效的日期:
      • isValid()方法:LocalDate类中的方法,用于判断一个日期是否是有效的日期。返回一个布尔值,true表示是有效日期,false表示不是有效日期。
  • 继承关系

    1. 继承关键字:

      • 在Java中,使用关键字extends来建立继承关系,子类通过继承关键字后跟父类名来继承父类的特性。
      • 语法:class SubClass extends SuperClass { ... }
    2. 继承的特性:

      • 子类可以继承父类的非私有属性和方法,包括实例变量、静态变量、实例方法和静态方法。
      • 子类可以通过继承直接访问父类的公共(public)和受保护(protected)成员。私有(private)成员无法被继承。
      • 子类可以重写(Override)父类的方法,即提供与父类方法具有相同名称、参数和返回类型的新实现。
      • 子类可以使用super关键字来调用父类的方法、构造函数和访问父类的属性。
      • Java中的继承是单继承的,一个类只能直接继承一个父类,但可以通过多层继承建立更复杂的继承关系。
    3. 继承的优点:

      • 代码重用:可以通过继承来避免重复编写相同功能的代码,提高代码的可维护性和复用性。

      • 继承和扩展:子类可以通过继承父类的特性来扩展功能,添加新的属性和方法。

      • 统一接口:通过继承可以创建类之间的层次结构,提供一致的接口来处理对象。

二、设计与分析

主要分析了几道具有代表性的题目

1.题目集1 ,7-7 判断三角形类型

题目:输入三角形三条边,判断该三角形为什么类型的三角形。

代码

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner triangle = new Scanner(System.in);
        float a = triangle.nextFloat();
        float b = triangle.nextFloat();
        float c = triangle.nextFloat();
        if( a < 1 || b < 1 || c < 1 || 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 && b == c ){
            System.out.println("Equilateral triangle");
        } else if ( a == b || a == c || b == c ) {
            if (Math.abs(Math.pow(a, 2) + Math.pow(b, 2) - Math.pow(c, 2)) < 0.000001 || Math.abs(Math.pow(a, 2) + Math.pow(c, 2) - Math.pow(b, 2)) < 0.000001 || Math.abs(Math.pow(c, 2) + Math.pow(b, 2) - Math.pow(a, 2)) < 0.000001) {
                System.out.println("Isosceles right-angled triangle");
            }
            else {
                System.out.println("Isosceles triangle");
            }
        } else if ((Math.pow(a, 2) + Math.pow(b, 2) == Math.pow(c, 2)) || (Math.pow(a, 2) + Math.pow(c, 2) == Math.pow(b, 2)) || (Math.pow(b, 2) + Math.pow(c, 2) == Math.pow(a, 2))) {
            System.out.println("Right-angled triangle");
        }
        else {
            System.out.println("General triangle");
        }
    }
}

分析过程:

  1. 导入java.util.Scanner类,用来接收用户输入。
  2. 创建一个名为Main的类。
  3. 在main方法中,创建一个Scanner对象triangle,用来接收用户输入的三个浮点数a、b、c。
  4. 使用triangle对象的nextFloat()方法分别将用户输入的三个浮点数赋值给变量a、b、c。
  5. 使用if语句进行条件判断:
    • 如果a、b、c中的任意一个小于1或大于200,则输出"Wrong Format",表示输入的数据格式错误。
    • 否则,继续进行下一步判断。
  6. 使用if语句判断是否构成三角形的条件:
    • 如果 a + b <= c 或 a + c <= b 或 b + c <= a,则输出"Not a triangle",表示不是一个三角形。
    • 否则,继续进行下一步判断。
  7. 使用if语句判断三角形的类型:
    • 如果a、b、c相等,则输出"Equilateral triangle",表示等边三角形。
    • 否则,继续进行下一步判断。
  8. 使用if语句判断是否为等腰直角三角形:
    • 如果任意两边的平方和等于第三边的平方,则输出"Isosceles right-angled triangle",表示等腰直角三角形。
    • 否则,输出"Isosceles triangle",表示等腰三角形。
  9. 使用if语句判断是否为直角三角形:
    • 如果任意两边的平方和等于第三边的平方,则输出"Right-angled triangle",表示直角三角形。
    • 否则,输出"General triangle",表示一般三角形。

2.题目集2,7-7 菜单计价程序-1

题目:

某饭店提供4种菜,每种菜品的基础价格如下:
西红柿炒蛋 15
清炒土豆丝 12
麻婆豆腐 12
油淋生菜 9

设计点菜计价程序,根据输入的订单,计算并输出总价格。
订单由一条或多条点菜记录组成,每条记录一行,最后以"end"结束
每条点菜记录包含:菜名、份额两个信息。
份额可选项包括:1、2、3,分别代表小、中、大份)

不同份额菜价的计算方法:
小份菜的价格=菜品的基础价格。
中份菜的价格=菜品的基础价格1.5。
小份菜的价格=菜品的基础价格
2。
如果计算出现小数,按四舍五入的规则进行处理。

代码类图:

2.7菜单

分析过程:

类的构造:

  1. 菜品类(Dish):
    • 属性:菜品名称(name)和单价(unit_price)。
    • 方法:getPrice(int portion)用来计算菜品价格,通过传入份额(1/2/3代表小/中/大份)来计算不同份额菜品的价格。
  2. 菜谱类(Menu):
    • 属性:菜品数组(dishes)保存所有菜品信息。
    • 方法:searchDish(String dishName)根据菜名在菜谱中查找菜品信息,返回Dish对象。
  3. 点菜记录类(Record):
    • 属性:菜品(dish)和份额(portion)。
    • 方法:getPrice()用来计算本条记录的价格。
  4. 订单类(Order):
    • 属性:记录数组(records)保存订单上每一道的记录,有效记录的数量(count)。
    • 方法:addARecord(Dish dish, int portion)用来添加一条菜品信息到订单中,getTotalPrice()用来计算订单的总价。
  5. 主类(Main):
    • 创建菜谱对象(Menu)并初始化菜品信息。
    • 创建订单对象(Order)。
    • 通过Scanner类读取用户输入的订单信息,并将订单信息添加到订单对象中。
    • 最后输出订单的总价。

解题思路:

  1. 定义菜品类(Dish),根据输入的份额计算菜品价格。
  2. 定义菜谱类(Menu),保存菜品信息。
  3. 定义点菜记录类(Record),保存每一条点菜记录的信息。
  4. 定义订单类(Order),保存订单上的所有点菜记录,并计算订单的总价。
  5. 创建菜谱对象、订单对象。
  6. 通过Scanner类读取用户输入的订单信息,根据菜名在菜谱中查找菜品信息,并将菜品信息和份额添加到订单对象中。
  7. 计算并输出订单的总价。

3.题目集2,7-8 jmu-java-日期类的基本使用

题目:

  1. 给定一个日期,判定是否为合法日期。如果合法,判断该年是否闰年,该日期是当年第几天、当月第几天、当周第几天、。
  2. 给定起始日期与结束日期,判定日期是否合法且结束日期是否早于起始日期。如果均合法,输出结束日期与起始日期之间的相差的天数、月数、念书。

代码:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoUnit;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 请求输入第一个日期
        String data = scanner.nextLine();
        // 请求输入第二个日期范围
        String dateRange = scanner.nextLine();

        boolean isFirstDateValid = isValidDate(data);

        // 判断第一个日期是否合法
        if (!isFirstDateValid) {
            System.out.println(data + "无效!");
        } else {
            // 解析第一个日期
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd").withResolverStyle(ResolverStyle.STRICT);
            LocalDate firstDate = LocalDate.parse(data, formatter);
            int firstDayOfYear = firstDate.getDayOfYear();
            int firstDayOfMonth = firstDate.getDayOfMonth();
            int firstDayOfWeek = firstDate.getDayOfWeek().getValue();

            // 判断是否为闰年
            if (firstDate.isLeapYear()) {
                System.out.println(data + "是闰年.");
            }
            // 输出第一个日期的相关信息
            System.out.println(data + "是当年第" + firstDayOfYear + "天,当月第" + firstDayOfMonth + "天,当周第" + firstDayOfWeek + "天.");
        }

        // 判断第二个日期范围是否合法
        String[] dates = dateRange.split(" ");
        String startDateStr = dates[0];
        String endDateStr = dates[1];

        if (!isValidDate(startDateStr) || !isValidDate(endDateStr)) {
            System.out.println(startDateStr + "或" + endDateStr + "中有不合法的日期.");
        } else {
            // 解析第二个日期范围
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd").withResolverStyle(ResolverStyle.STRICT);
            LocalDate start_date = LocalDate.parse(startDateStr, formatter);
            LocalDate end_date = LocalDate.parse(endDateStr, formatter);
            int startYear = start_date.getYear();
            int startMonth = start_date.getMonthValue();
            int startDay = start_date.getDayOfMonth();
            //System.out.println(startYear);
            int endYear = end_date.getYear();
            int endMonth = end_date.getMonthValue();
            int endDay = end_date.getDayOfMonth();

            // 判断结束日期是否不早于开始日期
            if (end_date.isBefore(start_date)) {
                System.out.println(endDateStr + "早于" +startDateStr  + ",不合法!");
            } else {
                // 计算相差的天数、月数和年数
                long daysDiff = ChronoUnit.DAYS.between(start_date, end_date);
                int diff_year = endYear - startYear;
                int diff_month = endMonth - startMonth;
                // 输出第二个日期范围的相关信息
                System.out.println( endDateStr + "与" + startDateStr+ "之间相差" + daysDiff + "天,所在月份相差" + diff_month + ",所在年份相差" + diff_year + ".");
            }
        }
    }

    // 判断日期字符串是否合法
    public static boolean isValidDate(String dateStr) {
        try {// try-catch 的好处在于,程序能够继续正常执行,不会因为异常而中断。
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd").withResolverStyle(ResolverStyle.STRICT);
            LocalDate.parse(dateStr, formatter);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

分析过程:

  • 首先,判断输入的第一行日期是否合法,使用isValidDate()函数判断,若不合法则输出错误信息。
  • 若第一行日期合法,则通过LocalDate.parse()解析日期,并使用相关方法获取年份、月份、天数等信息,判断是否为闰年,计算并输出当年第几天、当月第几天和当周第几天。
  • 接下来,判断输入的第二行日期范围是否合法,使用isValidDate()函数判断两个日期是否合法,若有不合法的日期则输出错误信息。
  • 若第二行日期范围合法,通过LocalDate.parse()解析日期,并获取开始日期和结束日期的年份、月份、天数,判断结束日期是否早于开始日期,若是则输出错误信息。
  • 若结束日期不早于开始日期,则使用ChronoUnit.DAYS.between()计算相差的天数,使用年份和月份的差值计算相差的月数和年数,并输出。

4.题目集3,7-2 课程成绩统计程序-1

题目:

某高校课程从性质上分为:必修课、选修课,从考核方式上分为:考试、考察。

考试的总成绩由平时成绩、期末成绩分别乘以权重值得出,比如平时成绩权重0.3,期末成绩权重0.7,总成绩=平时成绩0.3+期末成绩0.7。

考察的总成绩直接等于期末成绩

必修课的考核方式必须为考试,选修课可以选择考试、考察任一考核方式。

代码类图:

分析过程:

  1. 首先,创建了表示课程、成绩、班级和学生的类(Course、Score、Class、Student)。

  2. 在主函数中,使用Scanner类从用户输入读取数据。

  3. 创建了用于存储课程、成绩、班级和学生的列表(courses、scores、classes、students)。

  4. 进入while循环,循环条件是判断用户输入的下一行是否存在。

  5. 通过调用InputMatching类的matchingInput方法对输入进行匹配,根据返回的标志来处理不同类型的输入。

  6. 当输入是课程信息时,将输入的课程名称、课程类型和考核方式分别创建Exam_course或Course对象,并将其添加到课程列表courses中。

  7. 当输入是成绩信息时,将输入的成绩信息按空格分割,并提取学生ID、班级ID、学生姓名和课程名称等信息。

  8. 通过findOrAddClass方法找到对应的班级对象或创建新的班级对象。

  9. 创建学生对象,并判断学生是否已存在于学生列表中。

  10. 通过searchCourse方法查找课程对象,如果课程不存在,则输出错误信息。

  11. 如果课程存在,根据课程类型和成绩信息的长度来判断是考察还是考试,并创建对应的Inspect_course或Exam_course对象。

  12. 创建Score对象,并将其添加到学生的成绩列表中。

  13. 循环结束后,对学生列表进行排序,并遍历学生列表,计算学生的平均成绩并输出。如果学生没有参加任何考试,则输出相应的提示信息。

  14. 接着可以继续计算单门课程的平均成绩并输出。

三、采坑心得

​ 1.当输出格式很复杂的时候一定要注意输出的格式,在做题目集2的7-8 jmu-java-日期类的基本使用这题时 ,好几次都是输出的格式不对导致答案错误,因为这道题的输出分了很多种,而且涉及到的变量也很多。

比如这个测试用例的标点符号就很多,均采用英文的符号,并且没有空格。

所以说一定要注意输出格式要与题目所给的一致!!!不然这个原因导致错了就太疏忽了。

​ 2.pta中一些题目的测试用例其实与题目中所给的信息和要求是不一致的,所以有时候需要根据给的测试用例去反推题目要的输出是什么,这点真的很坑!!!

题目给的要求:

但是实际的测试用例:

四、主要困难以及改进建议

1.判断直角三角形

​ 在做第一次实验的第七题时,等腰直角三角形的测试点一直过不了,但是当时觉得等腰三角形,直角三角形的测试点都过了,测试等腰直角三角形的思路是将两个结合起来,按理来说应该是没错的。但是过不了,于是就开始改代码。

​ 考虑到原本直角三角形的判断是根据勾股定理a²+b²=c²,这样会不会因为输入的数是一个小数,而不是一个整数,导致计算出的结果小数后的数很多,判断小数相等的时候出了差错,于是,将代码逻辑改成|a²-b²|<0.000001后,测试点就过了!!!

原来的代码:

if((Math.pow(a,2) + Math.pow(b,2) == Math.pow(c,2)) || (Math.pow(a,2) + Math.pow(c,2) == Math.pow(b,2))|| (Math.pow(b,2) + Math.pow(c,2) == Math.pow(a,2))){
	System.out.println("Isosceles right-angled triangle");
}

改进后的代码:

if (Math.abs(Math.pow(a, 2) + Math.pow(b, 2) - Math.pow(c, 2)) < 0.000001 || Math.abs(Math.pow(a, 2) + Math.pow(c, 2) - Math.pow(b, 2)) < 0.000001 || Math.abs(Math.pow(c, 2) + Math.pow(b, 2) - Math.pow(a, 2)) < 0.000001) {
	System.out.println("Isosceles right-angled triangle");
}

2.菜单计价程序-1

2.1菜名识别:

难点:解题时需要根据输入的菜名在菜单中查找对应的菜品信息。如果订单中包含无法识别的菜名,就需要在输出总价前输出“** does not exist”,其中**是无法识别的菜名。

解决方案:代码中的searchDish()方法使用了一个for-each循环遍历菜谱中的所有菜品,如果找到了与输入菜名匹配的菜品,就返回该菜品的信息,否则返回null。在主函数中,根据用户输入的菜名,通过调用菜谱类的searchDish()方法来查找菜品信息,如果返回的结果为null,说明菜名不存在,就会在输出总价之前打印出菜名不存在的信息。如果菜名存在,则会将菜品和份额添加到订单中。

// 菜谱类
class Menu {
    Dish[] dishes;     // 菜品数组,保存所有菜品信息

    // 根据菜名在菜谱中查找菜品信息,返回Dish对象。
    public Dish searchDish(String dishName) {
        for (Dish dish : dishes) {
            if (dish.name.equals(dishName)) {
                return dish;
            }
        }
        return null;       // 没有找到菜品
    }
}

五、总结

通过前三次的pta,我学到了以下知识点和技能:

  • 输入输出流处理,包括使用Scanner类或BufferedReader类读取用户输入的数据,使用System.out.print()或System.out.println()打印结果。
  • 字符串处理,包括使用contains()方法判断字符串是否包含指定字符序列,使用split()方法将字符串按照指定的分隔符进行分割。
  • 对象的封装,包括定义类、属性和方法,并使用访问修饰符限制对对象的直接访问。
  • 使用HashSet类进行集合操作,包括添加元素、移除元素、判断集合中是否包含元素等。
  • 熟悉有参构造函数和无参构造函数的使用。
  • 使用java.time包中的LocalDate类进行日期相关的操作和判断。

对于上述提到的知识点,可以进一步学习和研究以下几个方面:

  • 学习更多关于字符串处理的方法,例如使用正则表达式进行复杂的匹配和替换操作。
  • 进一步学习面向对象编程的原理和思想,掌握封装、继承和多态等概念和技巧。
  • 学习更多Java的常用类和包,例如集合类的使用、异常处理、多线程编程等。
  • 深入学习日期和时间相关的类和方法,例如使用java.time包中的LocalDateTime类进行更灵活的日期和时间操作。

​ 我了解到这些题目是PTA题目,可能是作为练习和巩固Java基础知识的训练题目。尽管题目涵盖了多个知识点,但是在解决问题的过程中可以通过查阅Java官方文档、优秀的编程学习网站和一些参考书籍来帮助理解和掌握相关知识。对于难度较大的题目,可以尝试分解问题、思考解决思路、参考相关的示例代码和文章等来解决。

​ 希望课程能提供更加多样化和灵活的学习资源,例如视频教程、在线交流平台等,以便更全面和深入地学习和实践。

​ 总之,希望能够建立更加系统化和深入的学习体系,以便更好地掌握和应用Java相关的知识和技能。