OO第一阶段习题集总结

一、前言

大一下学期开始接触Java,通过看书、看MOOC课程学得一些Java语法, 因为有C语言的基础,可以很容易地写出一个简单的Java程序并通过编译正常运行。当然免不了嘴角上扬,沾沾自喜,心想:Java不过如此嘛。不过很快被打脸,三次题目集难度一直在上升,在写程序的时候也是困难重重的,遇到解决不了的问题就一直看书,CSDN一通查找,可见要想学好还是要投入时间细细钻研的。经过一个月的Java学习,给我最大的感受就是:一天不去学习Java,第二天就会觉得Java很生疏,所以必须每天坚持去学习实践。

三次题目集涉及的我认为值得注意 的知识点有数组(Arrays类),字符串(String类型):获取字符串长度,从字符串中获取字符,连接字符串,获取字串和字符串比较,方法、对象、类以及面向对象的思考。

题目集一是最简单的入门,一共有八道题,虽然看起来多,但写起来是很快的。没有复杂的算法,只是简单地考察基本的Java语法,而且大部分的题目是在学C语言时写过的,思路都很简单,唯一有卡壳是在7-8 判断三角形类型第一次做的时候有踩很多坑,但经过很多资料的查找和调试,程序通过了编译和PTA测试。在两天内利用课余零碎的时间全部完成。

题目集二相比题目集一,题量稍少,难度稍有提升,涉及到了数组、字符串与Boolean类型,字符串与数组相比C语言有所差异。做第一道题时陷入了"当局者迷"的状况,因为循环里的一个小错误调试了近3个小时。其中后三道题都是关于日期的题目,第一次做感觉难度很上头。自己一点一点地看教材,通过一些例子启发,历时三天,利用课余零碎时间完成。

题目集三是做的最糟糕的一次,老师在发布作业时已明确说明难度又有了提升,而且Deadline也延长到了10天,但没有引起足够的重视,第三都题没有做的很好。前两道题的所用到的知识点涉及到了类与类的封装性,这两块知识老师在课堂上有所讲解并举了实例,再通过课后看书,虽然做题过程中磕磕绊绊,但最终还是全部调试成功。因为一些课外活动,这周的课余时间不是充裕,前两道题历时三天完成。第三题简单多项式求导是三个题中最复杂的一个,涉及到了正则表达式,需要自学,对输入的字符串用正则表达式对式子进行匹配替换操作时困难重重,输出信息与要求不符合……最终失去耐心删掉了第一次做的源码,开始重新做,但在题目集时间截至时没有能够全部完成。至此反思,对于题目集三的时间投入还是不够多,不够耐心去钻研思考,容易烦躁。接下来的作业要吸取此次教训,全力以赴。

二、设计与分析

题目集一

 

1-7题都是入门级的难度,而且在学C语言的时候有做过,一次性通过编译,经过简单的调试通过PTA的检测。但由于还是沿用C语言的思维模式,部分源码中包含了大量的if判断语句及其嵌套,使得其复杂度上升,如2,4,8题。

 

第二题:7-2 电话键盘字母数字转换(最高复杂度54)

源码:

import java.util.Scanner;

public class Test {

    public static void main(String[] args) {

Scanner input = new Scanner(System.in);

String n = input.nextLine();

char i = n.charAt(0);

if(i == 'a'||i == 'b'||i == 'c'||i == 'A'||i == 'B'||i =='C')

    System.out.println("2");

else if(i == 'd'||i == 'e'||i == 'f'||i == 'D'||i == 'E'||i == 'F')

    System.out.println("3");

else if(i == 'g'||i == 'h'||i == 'i'||i == 'G'||i == 'H'||i == 'I')

    System.out.println("4");

else if(i == 'j'||i == 'k'||i == 'l'||i == 'J'||i == 'K'||i == 'L')

    System.out.println("5");

else if(i == 'm'||i == 'n'||i == 'o'||i == 'M'||i == 'N'||i == 'O')

    System.out.println("6");

else if(i == 'p'||i == 'q'||i == 'r'||i == 's'||i == 'P'||i == 'Q'||i == 'R'||i == 'S')

    System.out.println("7");

else if(i == 't'||i == 'u'||i == 'v'||i == 'T'||i == 'U'||i =='V')

    System.out.println("8");

else if(i == 'w'||i == 'x'||i == 'y'||i == 'z'||i == 'W'||i == 'X'||i == 'Y'||i == 'Z')

    System.out.println("9");

else

    System.out.println(i+" is an invalid input");

    }

}

 

分析:

这一题完完全全是学C语言时的思维模式,通篇if-else语句进行输入判断(惨不忍睹)。输入运用了字符串,用于输入大小写字母,再用.charAt将字符串中的字符提取出来,在if语句里进行单个比较。源码可以运用switch语句进行筛选,然后输出相应的数字,这样可以降低一些圈复杂度。

 

修改为switch语句后:

import java.util.Scanner;

 

public class W2 {

 

    public static void main(String[] args) {

        

Scanner input = new Scanner(System.in);

String n = input.next();

char i = n.charAt(0);

 

switch (i) {

case 'a':

case 'b':

case 'c':

case 'A':

case 'B':

case 'C':

    System.out.println("2");

    break;

case 'd':

case 'e':

case 'f':

case 'D':

case 'E':

case 'F':

    System.out.println("3");

    break;

case 'g':

case 'h':

case 'i':

case 'G':

case 'H':

case 'I':

    System.out.println("4");

    break;

case 'j':

case 'k':

case 'l':

case 'J':

case 'K':

case 'L':

    System.out.println("5");

    break;

case 'm':

case 'n':

case 'o':

case 'M':

case 'N':

case 'O':

    System.out.println("6");

    break;

case 'p':

case 'q':

case 'r':

case 's':

case 'P':

case 'Q':

case 'R':

case 'S':

    System.out.println("7");

    break;

case 't':

case 'u':

case 'v':

case 'T':

case 'U':

case 'V':

    System.out.println("8");

    break;

case 'w':

case 'x':

case 'y':

case 'z':

case 'W':

case 'X':

case 'Y':

case 'Z':

    System.out.println("9");

    break;

default :

    System.out.println(i+" is an invalid input");

}

结果:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

圈复杂度明显降低(由54降低到了10)

 

第四题:7-4 计算税率(最高复杂度32)

 

7-4部分源码

分析:

该题源码在判断每一种用户类型时都用到了if-else语句,其中还嵌套了判断收入的if-else语句,而且计算公式也夹杂在其中,使其复杂度骤增。在判断用户类型的时候可以使用switch语句来减少运算量,税率计算公式可以在if-else语句之前计算好,将其赋值给专门用来表示其意义的变量,以降低源码复杂度。

 

第八题:7-8 判断三角形类型(最高复杂度27)

源码:

import java.util.Scanner;

 

public class W8 {

 

    public static void main(String[] args) {

        Scanner input = new Scanner (System.in);

        double[] a = new double[3];

        for(int i = 0 ; i < 3 ; i ++ )

            a[i] = input.nextDouble();

 

        double x,y,z;

        x = a[0];

        y = a[1];

        z = a[2];

        

        double i = x * x + y * y - z * z;

        double j = x * x + z * z - y * y;

        double k = y * y + z * z - x * x;

        

        if(x<1||x>200||y<1||y>200||z<1||z>200)

            System.out.println("Wrong Format");

        else {

            if( x == y && x == z && y == x )

            System.out.println("Equilateral triangle");

            else if( (x == y && x != z) ||( x == z && x != y ) || ( y == z && y != x) )

            {

             if( Math.abs(i) < 0.0001 || Math.abs(j) < 0.0001 || Math.abs(k) < 0.0001 )

                 System.out.println("Isosceles right-angled triangle");

             else if( (x + y > z) && (x + z > y) && (y + z > x) )

                 System.out.println("Isosceles triangle");

             else

                 System.out.println("Not a triangle");

            }

            else if( Math.abs(i) < 0.0001 || Math.abs(j) < 0.0001 || Math.abs(k) < 0.0001 )

                System.out.println("Right-angled triangle");

            else if( (x + y > z) && (x + z > y) && (y + z > x) )

                System.out.println("General triangle");

            else

                System.out.println("Not a triangle");

        }

结果:

 

 

 

分析:

第八题在根据勾股定理判断直角三角形的计算中,没有考虑到当三角形边长为double类型时,由于double类型的字节长度,两边平方和可能与第三边的平方的差值不为0,造成逻辑判断错误,所以在此节点上花费了大量时间去优化,最终判定当其差值小于0.0001时,判定其为直角三角形。代码中还是if-else语句泛滥,使得圈复杂度偏高。第一次做的时候可能不会想这么多,但现在回头看以前写的代码感觉有好多地方可以去优化修改,也许是当时偷懒,没有尽力去想更好多方法。但在往后的编码中要注意代码复杂度及其质量。

 

题目集一总结:

尽量少用if-else语句以达到降低圈复杂度,提高代码质量的目的。

写代码时要充分思考,使用更简洁的方法。

 

题目集二

 

这次的题集相比题目集一的难度上升了挺多,尤其是后三道有关日期的题目,需要考虑多种情况,以至于全复杂度极高。

第一题:7-1 IP地址转换 

源码:

import java.util.Scanner;

 

public class W1 {

 

    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);

 

        int dec1,dec2,dec3,dec4,i,j,num;

        dec1 = 0;

        dec2 = 0;

        dec3 = 0;

        dec4 = 0;

        j = 0;

//        System.out.println("Enter the number:");

        String bin = input.nextLine();

        long [] bins = new long[32];

if(bin.length() != 32) {

            System.out.println("Wrong Format");

        System.exit(0);

        }

        for(i = 0; i < 32; i++) {

                if(bins[i] != 0 && bins[i] != 1) {

                    System.out.println("Wrong Format");

                 System.exit(0);    

                }

                

        for(i = 0; i < 32; i++) {

            bins[i] = Integer.parseInt(java.lang.String.valueOf(bin.charAt(i)));

        }

                }

 

                    for(i = 7; i >= 0; i--) {

                        num = (int) (Math.pow(2, i) * bins[j]);

                        dec1 += num;

              j++;        

                    }

                    System.out.print(dec1 + ".");

                    for(i = 7; i >= 0; i--) {

                        num = (int) (Math.pow(2, i) * bins[j]);

                        dec2 += num;

                        j++;

                    }

                    System.out.print(dec2 + ".");

                    for(i = 7; i >= 0; i--) {

                        num = (int) (Math.pow(2, i) * bins[j]);

                        dec3 += num;

                        j++;

                    }

                    System.out.print(dec3 + ".");

                    for(i = 7; i >= 0; i --) {

                        num = (int) (Math.pow(2, i) * bins[j]);

                        dec4 += num;

                        j++;

                    }

                    System.out.print(dec4);

                }

            

        }

结果:

 

 

分析:

整体结构较上一题目集规范整洁,对于输入的32位二进制数储存在了字符串中,并用.lehgth判断输入字符串中字符个数。然后利用.charAt将字符从字符串中提取出来存到一个数组中,通过四个for循环每次提取8个二进制数,再通过计算得出3位十进制数输出,得出最终结果。由于只使用了少量的if-else语句,并大量使用了for语句,代码的圈复杂度刚好为10。

 

第二题:7-2 合并两个有序数组为新的有序数组 

源码:

import java.util.Scanner;

 

public class W2 {

 

    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);

        int a;

//        System.out.println("输入第一个数组大小");

        int i = input.nextInt();

        int [] m = new int[i];

//        System.out.println("输入第一个数组数字";

        for(a = 0; a < i; a++)

            m[a] = input.nextInt();

//        System.out.println("输入第二个数组大小");

        int j = input.nextInt();

        int [] n = new int[j];

//        System.out.println("输入第二个数组数字");

        for(a = 0; a < j; a++)

            n[a] = input.nextInt();

 

        int [] l = new int[i + j];

        int num = 0;

         for (a = 0; a < i; a++) {

     l[a] = m[a];

     num++;

     }

     for (a = 0; a < j; a++) {

     l[num++] = n[a];

     }

     java.util.Arrays.sort(l);

     for(a = 0; a < i + j; a++) {

     System.out.print(l[a] + " ");

     }

        }

    }

结果

 

 

 

 

分析:

这道题第一次做就感到比较简单,思路清晰,写完代码经过简单调试提交通过PTA测试。按照题目要求获取输入数组的长度和存入其实中的数,然后重新声明一个全新的数组,长度为前两个数组长度之和,再利用for循环分别将前两个数组按顺序赋值给新的数组。最后调用arrays.sort方法将新数组进行排序,再用for循环输出。

 

接下来的三道题做的有那么点糟糕……

7-3 判断闰年及星期几 

7-3部分源码

结果:

 

 

7-4 求下一天 

结果:

 

 

7-5 求前N天 

7-5部分代码(使用了巨多的if-else语句,还加嵌套,使圈复杂度骤增)

结果:

 

 

分析:

三道题都与日期相关,有一定的相似性,都涉及到大月小月的天数,闰年与非闰年里2月的天数两个关键要素。三道题中题判断闰年(Boolean类型),判断二月天数,判断大小月的天数,相关计算都使用了if-else语句, if-else多次嵌套,这部分判断日期代码几乎一样。虽说写的时候逻辑思路很清晰,但这样写导致代码有好多地方都是重复的,提交PTA检测时也有多次运行超时。记得老师说过,代码有好多地方重复的话说明代码出了问题。第一次做的时候并没有引起重视,想着只要做出来能通过编译通过PTA测试就好了,并没有考虑后面的事情——代码的维护、调试。这三道题的代码在提交前调试虽然不是很难,但耗费了很多时间去逐个排查,修改代码找错误点也非常的费劲。上SourceMonitor一测,直接好家伙,7-5的圈复杂度上到了86,其他两道题的圈复杂度也都不低。归根结底还是做的少,经验不足,思维模式还停留在学C语言的阶段,再加上还有些偷懒,没有深入去思考更简洁的算法,只会用if-else去选择。其实好多if-else可以用switch选择和while循环等代替。接下来我会利用空闲时间去优化重构这部分圈复杂度极高的代码,积累经验。

 

题目集二总结:

经验少,第一次写新题要花费大量时间去翻书理解题目,效率地下。

面对比较复杂的题目没有去深入理解钻研,想当然写出的代码质量低,容易运行超时。

 

题目集三

 

目前为止,感觉这次题目集难度极高,加入了类与类的封装性的概念,刚开始比较难理解。尤其第三题,涉及到了需要自学的正则表达式,第一次没有写好,全盘推掉第一次的源码重新写,浪费了很多时间,导致在Deadline前没有写完,所以说本次题目集做的是很糟糕的。题目集三我特意在控制if-else语句的使用以提高代码质量和效率,虽然第二题最高复杂度到了20,但平均复杂度保持在了3以下。通过题目集三的联系,对类与类的封装性有了更深的理解。

 

7-1 创建账户类Account 

- 7-1代码类图

7-1部分源码

结果:

 

 

 

分析:

题目给了详细且明确的要求,根据要求去创建类和方法,明确需求后很快有了思路,虽然大部分代码为自动生成,但通过这道题我进一步理解了封装性的作用和好处。有一个小难点就是对于题目中要求的"账户开户时间,私有属性,LocalDate类型,默认为2020年7月31日",尝试了多种我已知的方法,但最终的输出结果都是"0-0-0"。通过多方查找资料,设置了"private LocalDate dateCreated = LocalDate.of(2020, 07, 31);"私有属性,在最后可以正确得出"The Account'dateCreated:2020-07-31"的结果。

 

7-2 定义日期类

7-2类图(我的类图<左>、题目给出的类图<右>)

 

7-2部分源码

 

结果:

分析:

根据题目给出的类图创建相应的类和方法,再加上在题目集二中做过求下一天的题目,所以在算法上没有很大难度,最主要是要注意运用类与类的封装性,年月日和月的最大天数都是私有属性,在Date类中不能出现相关变量。这次刻意避免了用if-else来判断日期,使用了switch语句,但还是出现了复杂的嵌套,最高圈复杂度可能就是因为switch的嵌套,但平均圈复杂度没有像题目集二中那么高,现在只有2.92。这斌不是最终结果,后续我还会去优化switch的嵌套部分。最终做出来源码的类图和题目中的全部相同,除了月份最大天数,关于每月的最大天数,我将数组中对应的2月份天数初始化为0,到后面判断闰年的时根据具体概况重新对其进行赋值。

 

7-3 一元多项式求导(类设计)

7-3完整类图

 

分析

说实话,这道题并没有真正的全身心去投入:首先对指导书没有进行一个钻研与深刻的思考,对题目要求一知半解,导致在做的过程中困难重重,做好的代码也是不合要求。可能是被正则表达式所困惑,弄错了侧重点,过程中一直致力于使用正则表达式,自己又把这道题目想的过于复杂了,以至于第一次写的代码到最后自己都看不懂,只好全部推翻重写,浪费了很多时间。时间没有合理的安排,没有在一个整块时间去做,以保证思路的模块性。总是用零零散散的时间去做,每次开始时总会花一段时间去想上一阶段做了什么,思维不连贯,效率低下。

这次的第三题对我来说就是一个教训,磨刀不误砍柴工,做题之前一定先要把题目理解清楚,明确需求;勤快一些,克服拖延症,不懂就问,不会就学。

 

题目集三总结:

了解了类的封装性并可以熟练地运用类去写代码,规避了前两次题目集的一些不当操作,平均圈复杂度在5以下。

需要且值得反思的是第三题,没能按时完成,接下来利用空闲时间继续完成。

 

三、踩坑心得

 

关于代码调试,我基本上都是在eclipse上测试PTA给出的测试数据,确认无误后再提交至PTA,所以有时可以提交一次便可通过测试。

第一次在PTA提交Java代码时有编译错误,这是因为代码中必须存在一个public class Main不允许出现其他的public class。将原来的名称改为Main即可通过检测。

源码提交过程中,由于题目集一、二的题目用了大量的if-else嵌套语句,降低了代码的运行效率,出现最多的是运行超时。

出现"答案错误"的一种情况是在编码过程中会忽略题目中的几项需求,导致输出结果与需求结果不符合。遇到这种情况,我经常是在eclipse里找特殊数据进行强测,找出错误段,进行修改补充。另一种情况便是因为输出格式问题:在PTA题目下的测试区进行运行测试,对比正确输出,找出错误,修改代码。

再者就是逻辑错误,在eclipse中调试便能发现问题,比如在题目集二中的第一题二进制转十进制:输入"11000110101001000011011011011001",输出为"255.255.0.255",而正确输出为"198.164.54.217",经过反复检查,发现是在for循环中i的范围取值错误导致输出错误,修改后得正确输出。

对面向对象概念的理解不是很深刻,写出的代码不符合面向对象的规则。

 还有很多在做题过程中踩的坑,在分析与设计中叙述了很多,在此不再赘述。

四、改进建议

 

对于题目集一、二的一部分改进有在题目集三中体现:尽量减少if-else语句的使用,提高代码质量。

加深对面向对象概念的理解,熟练掌握类、方法和封装性的应用。

勤加练习,深入体会,磨练意志,提高耐性,克服拖延症,合理安排时间,按时完成任务。

 

五、总结

 

总体来说,还是经验不足,知识点没有完全学扎实 ,写出的代码质量不是很高,在写代码的时候还会频繁地去翻书。

时间上,没有找到整块时间去全身心的投入,零零碎碎的时间导致写题思路不连贯,有些好的灵感经常被打断,效率不高。

虽然每周题量不是很多,但明显感觉课余时间都投入到了Java上,题目的难度刚开始做会觉得很难,无从下手,但慢慢去分析题目分析需求还是能找到眉目的。现在回头去看以前做的题目集又是另一个感觉,和当时第一次做的感觉完全不一样。

对于上课,紧跟老师的步伐,把每堂课的笔记空闲时去整理翻看,深入理解体会,自己重新写 一次老师写过的例子。

通过这次blog总结,发现了以前写的代码有好多的不足和可以优化改进的地方,也为下一步的学习指明了方向。

posted @ 2021-04-04 21:02  RS先森  阅读(102)  评论(0)    收藏  举报