一年中的第几天
今天的题,有些一言难尽啊……
1、看看题
我是题
题目写得挺详细的,给的case也挺好,照顾到了一些特殊情况。
2、审题
看完题目,各位应该都会会心一笑。
啊?就这就这?
到底是简单题嘛
题目没有藏什么陷阱。如果有,也只能说是和我们生活息息相关的一些常识问题——闰年。
作为格里高利历中特殊的一个年份,我们都知道闰年时,2月会出现第29天。而这也是题目唯一的烟雾弹了。
看看重点:
- 输入是一串字符串,题目保证了其正确性和格式(
YYYY-MM-DD
)。 - 输出的是该日期在当年中的天数,第一天则返回1。
- 注意闰年的2月份会多一天。
这题的重点几乎都在闰年的判断上了,我本来是这么认为的……
3、思路
很明显,今天的题目有两个点需要解决:
- 解析输入的字符串。
- 闰年的判断。
相信各位使用java语言开发的社畜,多少都有在业务中接触过日期字符串转换的需求。
我比较常用的是apache common包提供的StringUtils工具类,简单又刺激。
但是leetcode明显不会让我们用这些第三方的包……
总之,关于问题1,我们还有很多土办法。
由于题目保证了字符串的正确性,我们直接通过“-”拆分字符串,并将各个部分转型为int
即可。
问题2的话,就属于常识问题了(不懂可以百度 ),4的整数倍但不是100的整数倍,或为400的整数倍的年份,即为闰年。
开工!
4、撸代码
class Solution {
public int dayOfYear(String date) {
String[] seg = date.split("-");
List<Integer> info = Arrays.stream(seg).map(Integer::parseInt).collect(Collectors.toList());
Calendar calendar = new GregorianCalendar(info.get(0), info.get(1) - 1, info.get(2));
return calendar.get(Calendar.DAY_OF_YEAR);
}
}
这是我满心欢喜地写完的第一版,使用
Calendar
和其子类,能够帮我我们解决日期相关的几乎所有问题。
但是……丫的leetcode居然不支持使用Calendar !
你们用的到底是什么版本的jdk!高等语言的便利的工具类居然都不让用!不过用了还写什么算法啊.没办法,改改吧……
class Solution {
public int dayOfYear(String date) {
List<Integer> seg = Arrays.stream(date.split("-")).map(Integer::parseInt).collect(Collectors.toList());
//获取年月日
int year = seg.get(0);
int month = seg.get(1);
int day = seg.get(2);
//初始化每月的第一天所在的当年的第几天
int[] dayOfMonth = new int[]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int[] daysOfMonth = new int[12];
daysOfMonth[0] = 0;
for (int i = 1; i < 12; i++) {
daysOfMonth[i] = daysOfMonth[i - 1] + dayOfMonth[i - 1];
}
//注意闰年时2月要+1天
return day + daysOfMonth[month - 1] + (month > 2 && isLeapYear(year) ? 1 : 0);
}
/**
* 判断闰年的条件:
* 为400的整数倍,或为4但不为100的整数倍
*
* @param year 年份
* @return 是否为闰年
*/
private boolean isLeapYear(int year) {
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
}
}
于是,就出现了第二版土法子……
5、解读
isLeapYear()
方法用来判断年份是否为闰年。
判断逻辑也正如上面提到的:400的整数倍 或 4的整数倍但不为100的整数倍。
private boolean isLeapYear(int year) {
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
}
主函数中,我通过spilt
拆分字符串,并将各个部分转型为int
,这种写法有写耗时,但懒得改了。
List<Integer> seg = Arrays.stream(date.split("-")).map(Integer::parseInt).collect(Collectors.toList());
//获取年月日
int year = seg.get(0);
int month = seg.get(1);
int day = seg.get(2);
之后的步骤正如注释所说,是为了计算每月的第一天在当年是第几天。这里运用到了一点前缀和的思想。这样,我们只用知道日期所在的月份,就能快速得到该月的第一天在该年是第几天,加上日期就能得到最终答案。
//初始化每月的第一天所在的当年的第几天
int[] dayOfMonth = new int[]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int[] daysOfMonth = new int[12];
daysOfMonth[0] = 0;
for (int i = 1; i < 12; i++) {
daysOfMonth[i] = daysOfMonth[i - 1] + dayOfMonth[i - 1];
}
当然,也不要忘了闰年这个特殊情况。
//注意闰年时2月要+1天
return day + daysOfMonth[month - 1] + (month > 2 && isLeapYear(year) ? 1 : 0);
6、提交
时间排名惊人地低,但懒得优化了。
毕竟思路如此,我也能接受了。
7、咀嚼
时间复杂度和空间复杂度都是O(1),这应该没什么好说的。
8、偷知识可不算偷
鲸了!不能用Calendar
但居然能用LocalDate
!
这样真的能做到一句话写完了:
public int dayOfYear(String date) {
return LocalDate.parse(date).getDayOfYear();
}
以上,都是在题解里看到的其他大牛的解放。
除了依赖官方api的,其他的做法都大同小异
但还是分享一下其他大牛的以供参考
9、总结
总的收获不多,但善于使用各种语言丰富的api,会让你的算法简单很多。
除此之外,就算不用亲自实现,也请牢记各种思路,以备不时之需。只是这样还算是写算法吗?
简单题,简单地做。
祝大家冬至愉快,记得吃饺子哦。