LeetCode 1118. 一月有多少天
题目描述
题意分析
给定合法的年份
year和月份month,返回该年该月的天数。月份从一到十二,只求指定月份的长度,不是从年初累计到该月的天数。除二月以外,各月份的天数不随年份变化;二月在平年有二十八天,在闰年有二十九天。因此问题由固定月份查表与闰年判定两部分组成。
解法:闰年判断
核心思路
[!blue]
先把平年十二个月的天数放进固定数组。代码在第零项放一个占位零,使月份可以直接作为数组下标,不必在每次查询时做减一转换;合法月份不会读取占位项。
闰年规则包含逐层例外:通常每四年有一个闰年,但能被一百整除的整百年不按这条规则计算;其中能被四百整除的年份又仍然是闰年。所以条件是“能被四百整除”,或者“能被四整除但不能被一百整除”。
第二个分支排除普通整百年,第一个分支保留四百倍数年,二者用逻辑或连接。不能在整条公式之后再统一排除一百的倍数,否则连应当保留的四百倍数也会被排除。
最后只在月份为二月且年份为闰年时返回二十九,其他情况都使用平年表。这样年份修正只影响真正变化的月份,其余月份不需要分支处理。
解题步骤
- 创建带零号占位的平年月份天数表。
- 按四百倍数,或四的倍数且非一百倍数,计算闰年标记。
- 闰年二月返回二十九,否则返回表中该月份的天数。
代码实现
class Solution {
public int numberOfDays(int year, int month) {
// 第零项占位,使月份可以直接作为下标。
int[] days = {
0,
31,
28,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31
};
// 整百年需能被 400 整除,其他年份按 4 的倍数判断。
boolean leap = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
// 只有二月需要根据闰年增加一天。
if (month == 2 && leap) {
return 29;
}
return days[month];
}
}
func numberOfDays(year int, month int) int {
// 第零项占位,使月份可以直接作为下标。
days := []int{
0,
31,
28,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31,
}
// 整百年需能被 400 整除,其他年份按 4 的倍数判断。
leap := year%400 == 0 || (year%4 == 0 && year%100 != 0)
// 只有二月需要根据闰年增加一天。
if month == 2 && leap {
return 29
}
return days[month]
}
复杂度分析
- 时间复杂度:$O(1)$,表长度固定,只进行固定次数的取模、判断与一次查表。
- 空间复杂度:$O(1)$,月份表大小与输入年份无关。
关键点总结
[!green]
- 月份长度主要由固定表决定,只有二月需要年份修正。
- 四百倍数是整百年排除规则中的例外,必须作为独立可成立的分支。
- 月份编号与表下标一致,零号占位不代表真实月份。
易错点总结
[!yellow]
- 只判断能被四整除,会把普通整百年错误判成闰年。
- 只排除一百的倍数,却漏掉四百倍数分支,会错误排除一部分真正闰年。
- 在两个分支求或以后再统一排除整百年,仍会覆盖四百倍数的有效条件。
- 对所有月份都应用闰年加一天,会改变本应固定的其他月份。
- 天数表没有零号占位却直接使用月份下标,会让月份整体错位并在年底越界。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 1154. 一年中的第几天 | 简单 | 每月天数和闰年规则是计算年内序号的基础,本题只返回指定月份长度。 |
| 1360. 日期之间隔几天 | 简单 | 原题计算跨日期的总天数,本题提供月份与闰年的局部日期规则。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!