前三次作业总结

一、前言

  前三次作业整体来说比较简单,题量也在能够承受的范围之内,除了最后一道题之外都不算有多难,但是每个题目集我都没有能够拿到满分,因此,除去必要分析的题目,未满分的题目也是分析重点之一。

  在写前两次题目的时候,我没有什么复杂度的概念,想必根本经不起检测。甚至到现在,我对于降低复杂度的方法也是一知半解,这也是我今后努力学习的方向。

   在写前两次题目集的时候对于类的概念还不是很清晰,因此前两个题目集中只有Main类,因此分析重点主要在写题时的思路上。

二、设计与分析

1. 第一次作业(题目集1)

  • 7 - 4 计算税率

  这道题目也不陌生了,早在学习C语言的时候就已经接触过,因此这道题便先入为主的按照之前的做法进行。通篇都是if-else,当然也有我不知道怎么去简化的原因在里面。

附上复杂度测试:

   这道题主要分析重点不在复杂度处理上,因此不多赘述,下面来谈谈写这道题时遇到的问题。

   首先,这道题题目复杂,数字又多,因此在写或者计算上极易看错数据从而出错,因此后来我改变了算法,省去了人工计算的部分,但仍只能通过部分点。

  后来与同学交流,并在网上查找资料得知,JAVA中浮点数运算默认为double,而在程序中,float和double类型的运算往往都不准确。

  改进建议:可以尝试使用BigDecimal提供的方法改进运算

            BigDecimal number = new BigDecimal(Double.toString(input.nextDouble()));
            BigDecimal money = new BigDecimal(Double.toString(0));
            int i = money.compareTo(BigDecimal.ZERO);
            
            if(temp< 0 || temp > 3 || i == -1) {
                System.out.println("Wrong Format");
                System.exit(0);
           }

参考博客:JAVA中float、double使用注意问题

 

  • 7 - 8 判断三角形类型

   三角形类型的判断,想必大家都很熟悉,其他的三角形类型在判断时注意条件全面就可以了,唯一一个需要重点关注的便是等腰直角三角形了。

  由于这个三角形的两边相等,也因此它三边的长度总是有那么一两个数是无理数,所以想要人手动输入无理数,那是不可能的。

  一开始我便掉进了坑里,将条件设置为了a2+b2-c2 = 0,那结果肯定是总有那么一个点无法通过。

  后来我突然意识到,要是输入三边的话,遇到无理数,一般都是输入保留几位后的数字,比如1.41之类的,因此在设置条件是要注意误差的设置。因此将条件改为小于1

else if((a==b||b==c||c==a)&&(((a*a+b*b)-c*c<1)||((c*c+b*b)-a*a)<1||((c*c+a*a)-b*b<1))) {
                System.out.println("Isosceles right-angled triangle");
            }

  此时可以正常输出等腰直角三角形。

  

  虽然在保留整数的时候仍有的数据误差会大于1,但若是误差太大,计算也就没有意义了。

  后来我发现若是不对三个输入的数字进行判断的话,在判断条件里会有一大堆条件。

  改进建议:后续可以增加用户输入保留小数位数从而灵活处理精度的问题

  同时可以将输入的数据进行从小到大的排序从而只使用一个判断语句从而减少判断的次数,减小出错的可能。(弊端:排序可能会导致复杂度上升)

 2. 第二次作业(题目集2)

  • 7-2合并数组

  这道题改了很多次,改来改去还是会有的地方无法通过,之前一直不知道哪里出错了,刚刚调试了一下,突然出现了数组越界的问题

  输入数据的如果第一个数组比第二个小,输出的数是正确的

 

 

 但若是输入的第一个数组大小比第二个大2以上的话便会触发断点,接着报出数组越界的错误。

  

 

 

   经检查后发现是一个非常低级的错误,只需修改数组名称即可

  可见检查时一定要认真,检查的点也要全面!

  改进建议:由于目前使用的算法过于繁杂,需要将两个数组中的数据一一判断再存入新的数组,因此可以尝试将输入的数据全部存入同一个数组中进行排序

 

  • 7 - 4 求下一天

 

  这道题主要需要注意的点就是2月和12月以及闰年的情况,其余月份要注意是30天还是31天。需要判断的数据过多,因此要活用switch。

public static void nextDate(int year,int month,int day) {
        switch(month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
            if(day == 31) {
                day = 1;
                month ++;
            }else day ++;break;
         case 12:
            if(day == 31) {
                day = 1;
                month = 1;
                year ++;
            }else day ++;break;
        case 4:
        case 6:
        case 9:
        case 11:
            if(day == 30) {
                day = 1;
                month ++;
            }else day ++;break;
        case 2:
            if(isLeapYear(year) == true && day == 29) {
                    day = 1;
                    month ++;
                }else if(day == 28) {
                        day = 1;
                        month ++;
                    }else day ++;break;
        }
        System.out.print("Next date is:" + year + "-" + month + "-" + day);
}                                     

  但即便是使用switch,也难免会涉及到 if - else 的使用,因此复杂度依然没有在10以下。并且嵌套使用判断过多,代码也显得有些复杂难懂,不符合代码规范。

 

 

   一开始我傻乎乎的以为非法输入就是题目上所给出的范围,提交一次之后便明白了,毕竟题目所能够给出的内容是有限的,真正的要求还是要自己去体会,有时候考虑的越多,能照顾的方面也就更加全面。

  这次题目给出了方法的设计,也算是打开了我脑海中的一扇门,一个方法往往是不能解决所有的问题的,因此需要多个方法相互配合,另外,题目还让我了解到了boolean类型。

  改进建议:可将日期操作另放入一个类中,从而使程序结构更加合理。同时可以尝试其他的方法进行判断,慢慢减少if的使用。

  • 7 - 5求前N天

  这道题和7 - 4有很多共同点,但也有区别,最大的区别就是在考虑2月和12月的基础上,还要考虑更多的因素。

  

 

 

   代码和上一题相似,但是多了更多的判断,复杂度与上一题差不多,不过更多的可能是因为这道题我考虑的并不全面导致很多判断仍未加入进去。减少复杂度需要减少if判断语句的个数。

    public static void passDate(int year,int month,int day,int n) {
        switch(month) {
        case 1:
            if((day - n) < 1) {
                
                    day = day - n + 31;
                    month = 12 ;
                    year --;
                    break;
            }
        case 3:
            if(isLeapYear(year) == true && (day - n) < 1) {
                day = day - n + 29;
                month --;    
            }
            if(isLeapYear(year) == false && (day - n) < 1) {
                day = day - n + 28;
                month --;
            }break;
            
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            if((day - n) > 31  ) {
                day = day - n - 31;
                month ++;
                if(month == 13) {
                    year ++;
                    month = 1;
                }
            }else if((day - n) < 1) {
                day = day - n + 31;
                month --;
            }else day -= n; break;
        
        case 4:
        case 6:
        case 9:
        case 11:
            if((day - n) > 30  ) {
                day = day - n - 30;
                month ++;
            }else if((day - n) < 1) {
                day = day - n + 31;
                month --;
            }else day -= n; break;
        
        case 2:
            if(isLeapYear(year) == true && (day - n) > 29) {
                day = day - n - 29;
                month ++;
            }else if(isLeapYear(year) == true && (day - n) < 1) {
                day = day - n + 31;
                month --;    
            }
            else if(isLeapYear(year) == false && (day - n) > 28) {
                day = day - n - 28;
                month ++;
            }else if(isLeapYear(year) == false && (day - n) < 1) {
                day = day - n + 31;
                month --;
            }
            break;
        }
        
        System.out.print(n + " days ago is:" + year + "-" + month + "-" + day);
    }
}

  一开始我只考虑了1、2、12月的日期变化,但是仍有点无法通过。后来经过思考,突然意识到,3月若是向前减去几天到了2月之后,应用29或是28来减,而不是和其他月份一样用30来减。当然上面给出的5、7、8、10、12月的向前减的代码中仍有错误,当day-n小于时,日期应从30开始减而不是31。

  同理,4、6、9、11月向前减若是减到上一个月应为31,因为题目中有这个测试,因此这个点被考虑到了。

  但是还有个情况,便是7、8月份,8月向前一个月,仍要从31开始减掉剩余日子,由于测试点未给出八月之外的情况,因此导致我这个错误的代码捡了个漏子

 

 

   正确的代码应该将8月单独分出来,使用我上面错误的判断,将剩下代码修改正确后便能够通过每个测试。

  改进建议:关于降低复杂度,其实可以发现,每个判断是不是到下个月或是上个月的语句都有异曲同工的感觉,因此可以单独写一个函数,将这些判断化为一个函数,再通过不同月份输入不同的天数从而进行更简便的判断。

3.第三次作业(题目集3)

  • 7 - 2定义日期类

  这道题是根据类图写出来的程序,在创建类和方法时当然是方便了许多,不过由于在判断输入非法时在主函数中多出了个判断导致复杂度还是比题目集2中的要高一些

 

 

   经过检查代码后,发现可以在判断非法的时候便输出

    public boolean checkInputValidity() {
        isLeapYear(this.year);
        if(this.year < 1900 || this.year > 2020|| this.month < 1 || this.month > 12 || this.day > mon_maxnum[this.month] || this.day < 1) 
            return false;
        else 
            return true;
    }

  在return false前加上输出语句,将主函数中的判断直接true的话就运行后面的方法。

 

 

   在修改后,main中复杂度下降1

 

 

 

  在主要算法中,处理方法和上一题目集相同,改进方法也可以参照上一题目集中这道题,也可以新建函数从而减少判断次数。

  一开始并没有注意到题目中给出的储存每个日期的数组,导致非法判断写了很长,最后用一句this.day > mon_maxnum[this.month] 就可以处理

  类具有封装性,其中的成员变量受到保护, 外界无法访问,其中有很多好处,现阶段我们使用的时候直接感受到的好处便是降低了类之间的耦合,并且对于成员可以更灵活的处理,也给了我们更多发挥细节的空间。

  • 7 - 3  一元多项式求导

  这道题由于种种原因,我高看自己的效率了,导致最后只能实现一个非法输入。

  关于这道题的类设计,一开始我是毫无头绪的,想着走一步试一步,因此一开始只设计了一个累,也就是输入的字符串分一个类,在这里进行所有的操作。当然事实告诉我们这个方法是不可行的。后来经过讲解,才意识到,每种数据类型都要分成类。

  目前对于这道题的解法脑海中有一个大致的思路,但仍未完全实现,将字符串分割后按照类型分别传入相应类中进行操作。

  当然,由于效率以及学习方法存在问题,代码到现在仍未能够较完整的实现,因此先不贴出来丢人了。

 

三、总结

  在写题的过程中,我充分认识到了自己行事效率的底下,尤其是第三次题目,直接导致了程序功能无法实现完全,甚至是只能实现最基础的错误判断。

 

  在这三次题目集中,我也学到了很多,最起码是获得了很多的教训,比如做事要提早。当然,这是最基础的部分,更重要的是我明白了学习靠的还是自己,不能总是等老师把所有的知识摆到面前了也不一定去学,要主动的去寻找知识。

  另外,老师发的每个东西都可能会使我们的学习效率事半功倍,因此一定要及时的去看,去学习!

  还有,学习不一定只靠自己,适当的和同学交流,交换思路,有时候可以将我们从一个死胡同里捞出来,豁然开朗。这几次有的题目就是交流后打开了另一个思路。

  最后,当我们面临一个一直无法解决的问题的时候,不妨先放下,先处理后面的事情之后再拐回来,就比如这次前两个题目集没有满分的题目,当时一直没有解决的东西,过了这段时间回头看,突然很快就能找到解决的方法。

  今后一定要提高办事情的效率,早些开始写题!