中级农民
- 积分
- 166
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2014-3-9
- 最后登录
- 1970-1-1
|
这里介绍一种常数时间复杂度的difference函数实现:
由于除了闰年都是1年365天,因此这里的关键是在于判断两个年份之间闰年的个数:
闰年个数 = 2年份间所有4的倍数 - 2个年份间经过的所有100的倍数 + 2年份间经过的所有400的倍数。
所以关键的问题又转化为了如何判断两个数a和b之间的c的倍数的个数,注意到对任意的a和c:
在a到a+c-1之间(含边界)有且只有1个c的倍数;
在a+c到a+2c-1之间(含边界)有且只有1个c的倍数;
...
在a+kc到a+(k+1)c-1之间(含边界)有且只有1个c的倍数;
于是可以知道:在a到a+kc-1之间(含边界)一定有且只有k个c的倍数;
然后注意到另一个事实:一定存在1个整数n,使得a+nc-1<b<=a+(n+1)c-1成立。
根据上面的结论,可以知道,只要找到这个n,那么a和b之间c的倍数个数(含边界)就等于:n 加上 (a+nc)和b之间c的倍数个数。而要求 (a+nc)和b之间c的倍数个数,只需要遍历 (a+nc)到b,逐个检查是否能够被c整除,最多也只要循环c次就能求得结果。
因此只要能求得上述的n的值,就能以O(c)的复杂度计算出两个数a和b之间的c的倍数的个数。
而n实际上非常好求,注意到有:n = [(b+1-a)/c],于是我们得到了一个以O(c)的时间复杂度计算两个数a和b之间的c的倍数的个数的算法。
而在闰年个数的计算中,c分别等于4,100以及400,皆是常数,于是我们得到了一个常数时间复杂度O(1),计算两个给定的年份之间所经历的闰年个数的算法,进而可以常数时间复杂度来计算difference的值。
下面贴上我的代码:
public static int countDivideBy(int startNumber, int endNumber, int divideNumber) {
if(startNumber > endNumber) {
return 0;
}
int kNumber = (endNumber + 1 - startNumber) / divideNumber;
for(int i = startNumber + kNumber * divideNumber - 1;i <= endNumber;i++) {
if(i % divideNumber == 0) kNumber ++;
}
return kNumber;
}
public static int countLeapYear(int startYear, int endYear) {
return countDivideBy(startYear,endYear,4) - countDivideBy(startYear,endYear,100) + countDivideBy(startYear,endYear,400);
}
public int difference(Date d) {
if(!isValidDate(d.month,d.day,d.year)){
System.out.println("Invalid date value!");
System.exit(0);
}
int dayNumberOfYearD = 365;
int dayNumberOfYearThis = 365;
if(isLeapYear(d.year)) dayNumberOfYearD++;
if(isLeapYear(this.year)) dayNumberOfYearThis++;
if(this.year == d.year) return this.dayInYear() - d.dayInYear();
if(this.year > d.year) {
return this.dayInYear() + (dayNumberOfYearD - d.dayInYear()) + 365*(this.year - d.year - 1) + countLeapYear(d.year+1,this.year-1);
} else {
return -(dayNumberOfYearThis - this.dayInYear()) - d.dayInYear() - 365*(d.year - this.year - 1) - countLeapYear(this.year+1,d.year-1);
} // replace this line with your solution
}
|
|