作业总结1
第一次作业题目量较大,知识点较广(1,2,4,12题选择结构。3,7,9题循环结构。5,6,8,10题对字符串的操作。)但题目难度不是特别高。
第二次作业题目量较少,偏向于对测试点的考虑(测试点较多),题目相对简单。
第三次作业题目量少,要求对程序的运行更高效。
设计与分析
第三次作业7-3时要考虑各种特殊日期:每月最后一天(第二天月份+1,日期归1),每年最后一天(年份+1,月份归 1,日期归1),2.28(如果是平年,按每月最后一天算,如果是闰年日期+1),2.29(平年非法输入,闰年按每月最后一天算)。
1 public void getNextDate() { 2 if(checkInputValidity()) {//checkInputValidity()判断日期是否合法 3 if(month==2&&isLeapYear(year)&&day==29) { 4 System.out.println("Next day is:"+year+"-3-1"); 5 return; 6 } 7 else if( month==12&&day==31) { 8 System.out.println("Next day is:"+(year+1)+"-1-1"); 9 return; 10 } 11 else if(day==mon_maxnum[month]&&(month!=12)) {//mon_maxnum是int型数组记录平年的月末天数{0,31,28,31,...,30,31} 12 if(isLeapYear(year)&&month==2) { //isLeapYear判断是否为闰年 13 System.out.println("Next day is:"+year+"-"+month+"-"+(day+1)); 14 } 15 else { 16 System.out.println("Next day is:"+year+"-"+(month+1)+"-1"); 17 } 18 return; 19 } 20 else { 21 System.out.println("Next day is:"+year+"-"+month+"-"+(day+1)); 22 } 23 } 24 else { 25 System.out.println("Date Format is Wrong"); 26 } 27 } 28 }
第三次作业7-4是7-3的plus版本,难度较大。
分析:对于求下n天可以先让日期+n,当n与日较小(如3.1的下3天是3.4)可以直接得到答案。但是当n和日较大时,比如3.28的下5天 ,得到的是3.33。我们都知道3月是没有33天的。如果让小朋友算,他们会扳手指头29,30,31,1,2这样数5天。所以很自然的想到,当+n后日期非法了应该减去这个月的日期数,月+1(3.28+5=3.33 => (3+1)月(33-31)日 => 4.2)。
1 public DateUtil getNextNDays(int n){ 2 DateUtil nextDay=new DateUtil(this.year,this.month,this.day); 3 nextDay.day+=n; 4 5 while(!nextDay.checkInputValidity()) { // nextday+n后如果非法就进入循环 6 7 if(nextDay.month==2 && nextDay.isLeapYear(nextDay.year)) { 8 nextDay.month++; 9 nextDay.day=nextDay.day-29; 10 }// 润2月 11 12 else if( nextDay.month==12) { 13 nextDay.year++; 14 nextDay.month=1; 15 nextDay.day=nextDay.day-31; 16 }// 12月 17 18 else {// mon_maxnum是int类型数组记录月末天数{0,31,28,31,30,...,30,31} 19 nextDay.day=nextDay.day - mon_maxnum[nextDay.month]; 20 nextDay.month++; 21 }// 其他月份 22 } // 日期合法后跳出循环 23 24 return nextDay; 25 }
于是我们得到了上面这串代码,但是当n很大时比如 300w 那么这个代码一次减小30,大约要近10w次才能跳出循环效率低下。所以我们想到 4年一润和400年一润 可以把周期定为400年(每100年会存在平年所以不用4年做一个周期)。400年里有100-3=97个闰年也就是 365*400+97=146097天。
if(nextDay.day > 150000) { int o=0,u=0; o=nextDay.day/146097; u=nextDay.day%146097; nextDay.day=u; nextDay.year=nextDay.year+400*o; // nextDay.year=nextDay.year+400; // nextDay.day=nextDay.day-146097; } //n很大 else {...}
146097似乎也是一个很大的数,如果我的n就等于146096那程序也要运行很久所以我们可以计算14w天里大概有多少年和多少闰年。所以上面else{...}里可以加入计算年的方法
1 if(nextDay.day>800) { 2 nextDay.day=nextDay.day-400; 3 DateUtil date1=new DateUtil(); // 记录初始年份 4 DateUtil date2=new DateUtil(); // 记录n-400天后年份 5 int o=0,u=0,leapMonth=0; 6 o=nextDay.day/365; 7 u=nextDay.day%365; 8 9 date1.day=nextDay.day; 10 date1.month=nextDay.month; 11 date1.year=nextDay.year; 12 13 nextDay.year=nextDay.year+o; 14 15 date2.day=nextDay.day; 16 date2.month=nextDay.month; 17 date2.year=nextDay.year; 18 19 for(int i=date1.year;i<=date2.year;i++) { 20 if(isLeapYear(i)) { 21 leapMonth++; 22 } 23 } // 记录n-400天后有多少闰年 24 //减去开始和结尾可能不会经历的润2月 25 if(date1.month>=3&&isLeapYear(date1.year)) { 26 leapMonth--; 27 } 28 if(date2.month<3&&isLeapYear(date2.year)) { 29 leapMonth--; 30 } 31 32 nextDay.day=u-leapMonth+400; // 开始n减去了400,现在补加回来 33 }
这样我们要计算的天数最多就是800天了.
踩坑心得
上面的代码可能会有些疑惑:为什么要减去400天?
当输入上述测试用例(1999.3.28的后6543天)后 黄色部分day 变为负数了,但我们并没有做对于负数的处理(这个在前n天会使用到),这样进入while()循环后就会变成死循环.而400年里有397个闰年所以leapMonth最大为397减去部分大于397就可以.至于day大于800是为了让day减去400后能保证有一年.
求上n天就异曲同工了
1 public DateUtil getPreviousNDays(int n) { 2 DateUtil previousDay=new DateUtil(this.year,this.month,this.day); 3 previousDay.day -= n; 4 while(previousDay.day<=0) { 5 if(Math.abs(previousDay.day) > 150000) { 6 previousDay.year=previousDay.year-400; 7 previousDay.day=previousDay.day+146097; 8 } 9 else { 10 if(previousDay.month==3&&previousDay.isLeapYear(previousDay.year)) { 11 previousDay.month--; 12 previousDay.day=previousDay.day+29; 13 } 14 else if( previousDay.month==1) { 15 previousDay.year--; 16 previousDay.month=12; 17 previousDay.day=previousDay.day+31; 18 } 19 else { 20 previousDay.month--; 21 previousDay.day = previousDay.day+mon_maxnum[previousDay.month]; 22 } 23 } 24 } 25 return previousDay; 26 }//取得year-month-day的前n天日期
至于求两天之间的差的天数,在这之前我们要先比较这两天的先后顺序,首先我们想到的是先比较年再比较月再比较日,但我们可以使用数学里加权重的思想让年的权重>> 月的权重>>日的权重.
1 public boolean compareDates(DateUtil date) { 2 if( (this.year*400+this.month*31+this.day) <= (date.year*400+date.month*31+date.day) ){ 3 return true; 4 } 5 else { 6 return false; 7 } 8 }//比较当前日期与date的大小(先后)true this先;
接着我们去考虑两天差的天数
1 public int getDaysofDates(DateUtil date) { 2 if(this.equalTwoDates(date)) { 3 return 0; 4 } 5 DateUtil date1=new DateUtil(); //记录前面的时间 6 DateUtil date2=new DateUtil(); //记录后面的时间 7 if(this.compareDates(date)){ 8 date1.day=this.day; 9 date1.month=this.month; 10 date1.year=this.year; 11 12 date2.day=date.day; 13 date2.month=date.month; 14 date2.year=date.year; 15 } 16 else { 17 date2.day=this.day; 18 date2.month=this.month; 19 date2.year=this.year; 20 21 date1.day=date.day; 22 date1.month=date.month; 23 date1.year=date.year; 24 } 25 26 int leapMonth=0,n=0; 27 int[] monthDay=new int[] {0,0,31,59,90,120,151,181,212,243,273,304,334}; // monthDay是记录平年月份所拥有的天数 28 n=(date2.year-date1.year)*365 + (monthDay[date2.month]-monthDay[date1.month]) + (date2.day-date1.day); 29 for(int i=date1.year;i<=date2.year;i++) { 30 if(isLeapYear(i)) { 31 leapMonth++; 32 } 33 } 34 if(date1.month>=3&&isLeapYear(date1.year)) { 35 leapMonth--; 36 } 37 if(date2.month<3&&isLeapYear(date2.year)) { 38 leapMonth--; 39 } 40 n=n+leapMonth; 41 return n; 42 }//求当前日期与date之间相差的天数
这部分代码大部分内容都在求n天后解释过了,就不多赘述.27行的monthDay(比如我们计算3.26号是这年的第几天会这样算:26+前两个月)记录的就是每月的前n-1个月有多少天.
总结:
学到了什么: 这三次练习,让我了解了Java的基础知识(选择结果,循环结构,字符串,数组),还有Java的class(这个类似与c语言的struct但又比struct更加方便,功能性也更强).
不足: 对于算法效率不够高,有些题目因为运行次数太多超时.
意见: 希望老师每次作业截止后把答案或者优秀的算法分享出来.