目录

题目描述

1118. 一月有多少天

题意分析

给定年份 year 和月份 month(1 到 12),返回该年该月的天数。

这道题的信息量几乎全在「哪些是常量、哪些是规则」的划分上。十二个月里有十一个月的天数与年份无关,是彻底的常量;只有二月依赖年份,取 28 或 29。唯一的变量是「这一年是不是闰年」,除此之外没有任何需要计算的东西。

闰年的判定规则本身是题目给定的历法知识而非可推导的数学:能被 4 整除的是闰年,但能被 100 整除的不是,能被 400 整除的又是。三条规则是层层例外的关系,写成布尔表达式时的组合顺序会直接决定对错——这就是本题真正的考点,也是它虽然标简单却常被写错的原因。

约束里 year 的范围是 1583 到 2100。这个下界不是随便定的:1582 年格里高利历改革之前用的是儒略历,闰年规则里没有「百年不闰」这一条,题目通过限定范围避免了历法切换带来的歧义。上界 2100 则恰好包含一个「能被 100 整除但不能被 400 整除」的年份,说明测试数据一定会覆盖这个最容易写错的分支。

边界:month 保证在 1 到 12 之间,不需要做参数校验;2100 年是平年(能被 100 整除、不能被 400 整除),2000 年是闰年(能被 400 整除),这两个值是必须自测的用例。

解法:闰年判断

核心思路

月份天数中,只有二月受年份影响。用长度为 13 的表保存平年各月天数,第 0 位占位,使月份可以直接作为下标;然后只处理“闰年二月加一天”这一条例外。

格里高利历的闰年条件为:

\[leap=(year\bmod 400=0)\lor(year\bmod 4=0\land year\bmod 100\ne0)\]

第一项保留 400 的倍数,例如 2000;第二项接受普通的 4 的倍数,同时排除 1900、2100 这类非 400 倍数的整百年。括号体现了“能被 400 整除,或能被 4 整除但不能被 100 整除”的完整规则。

正确性按月份分类即可:非二月直接查表,表值固定正确;二月若 leap 为真返回 29,否则读取表中的 28。两类覆盖所有合法输入,因此算法总能返回正确天数。

解题步骤

  1. 建立 days = {0,31,28,31,30,31,30,31,31,30,31,30,31},把 28 作为二月默认值。
  2. 计算 leap = year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
  3. month == 2 && leap,返回 29;否则返回 days[month]

边界样例必须覆盖整百年:2000-02 命中 400 规则,返回 29;1900-022100-02 被 100 规则排除,返回 28;2024-02 返回 29;2023-02 返回 28。还应检查 2000-01 返回 31,避免把“闰年”错误地作用于所有月份。

代码实现

class Solution {
    public int numberOfDays(int year, int month) {
        int[] days = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        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}
	leap := year%400 == 0 || (year%4 == 0 && year%100 != 0)
	if month == 2 && leap {
		return 29
	}
	return days[month]
}

复杂度分析

  • 时间复杂度:$O(1)$,只进行常数次取模、判断和数组访问。
  • 空间复杂度:$O(1)$,月份表长度固定为 13。

关键点总结

  • 固定月份数据用表表达,控制流只保留二月的闰年例外。
  • 闰年条件是“400 的倍数,或 4 的倍数且不是 100 的倍数”,括号不能改变。
  • 第 0 位占位让月份与下标一致,避免 month - 1 的差一错误。
  • 面试自测至少覆盖普通年、普通闰年、非闰整百年和 400 倍数年。

易错点总结

  • 只判断 year % 4 == 01900-022100-02 会被误判为 29 天。
  • 漏掉 400 的例外:year % 4 == 0 && year % 100 != 0 会把 2000-02 误判为 28 天。
  • 错写括号:(year % 400 == 0 || year % 4 == 0) && year % 100 != 0 同样会排除 2000 年。
  • 只判断闰年、不判断 month == 22000-01 会错误返回 29。
  • 表首有占位元素时必须访问 days[month];若改用 12 个元素,则必须访问 days[month-1],两种约定不能混用。

相似题目

题目 难度 考察点
1154. 一年中的第几天 简单 同样依赖闰年判定,但需要把前几个月的天数累加,多一层前缀和思路
1360. 日期之间隔几天 简单 把日期折算成「距某个基准日的天数」再相减,闰年规则要反复使用
507. 完美数 简单 同为规则型数学题,考点在因数枚举的边界而非历法
728. 自除数 简单 逐位取模判断,练习「把规则准确翻译成布尔表达式」
168. Excel 表列名称 简单 从 1 起编号的进制转换,与本题「表首位补 0」同属下标对齐类陷阱
412. Fizz Buzz 简单 多条整除规则叠加,判断顺序写反就会出错,与闰年的层层例外同构