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修饰符将数据域声明为私有的,这称为数据域封装。所以从现在开始,

  除非特别的原因而另外指定,否则所有数据域--私有、构造方法和方法--公共。

posted @ 2021-04-04 21:19  WeirdTong  阅读(182)  评论(0)    收藏  举报