题目描述

✅ 1185. 一周中的第几天

image-20260929110118464

题意分析

输入日、月、年,返回对应星期的英文名称。题目保证日期有效且位于 1971 到 2100 年,并给出基准:1971 年 1 月 1 日是星期五。只要算出目标日期比基准晚多少天,再按七天一周期平移即可。

解法一:累计天数与闰年计数

核心思路

[!blue]

令 days 为从 1971 年 1 月 1 日到目标日期已经经过的天数,基准当天记为零。把它分为三部分:此前完整年份的天数、目标年份中此前完整月份的天数,以及本月已经过去的 day - 1 天。

完整年份共有 year - 1971 个,先按每年 365 天计数,再补上其中闰年的额外一天。公历闰年满足“能被四整除且不能被一百整除,或者能被四百整除”。因此截至年份 y 的闰年个数是 y/4 - y/100 + y/400,这里均为整数除法:先计四的倍数,再扣掉整百年份,最后加回整四百年份。用截至 year - 1 的数量减去截至 1970 的数量,就得到本次需要补入的整年闰日数。

月份用平年长度表累加目标月之前的月份。若目标年是闰年,而且目标月在二月之后,再补上一天;一月、二月不能提前补,否则会把尚未经过的二月二十九日算进去。目标日自身不算已经过去,所以最后加入的是 day - 1。

星期数组按周日到周六排列,下标为零到六,基准星期五对应下标五。目标星期下标就是 (days + 5) % 7。日期范围保证 days 非负,不需要处理负数取模;所有累计天数也都在普通整数范围内。

解题步骤

  1. 准备七个英文星期名称与十二个月的平年天数。
  2. 计算完整年份的 365 天部分,再用闰年计数差补入闰日。
  3. 累加目标月之前的完整月份;目标年为闰年且月份大于二时再加一天。
  4. 加上 day - 1,返回星期数组中 (days + 5) % 7 对应的名称。

代码实现

class Solution {
    public String dayOfTheWeek(int day, int month, int year) {
        String[] weekdays = {
            "Sunday",
            "Monday",
            "Tuesday",
            "Wednesday",
            "Thursday",
            "Friday",
            "Saturday",
        };
        int[] monthDays = {
            31,
            28,
            31,
            30,
            31,
            30,
            31,
            31,
            30,
            31,
            30,
            31,
        };
        int lastYear = year - 1;
        int leapYears = lastYear / 4 - lastYear / 100 + lastYear / 400
                - (1970 / 4 - 1970 / 100 + 1970 / 400);
        int days = (year - 1971) * 365 + leapYears + day - 1;

        for (int m = 1; m < month; m++) {
            days += monthDays[m - 1];
        }

        if (month > 2 && (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))) {
            days++;
        }

        return weekdays[(days + 5) % 7];
    }
}
func dayOfTheWeek(day, month, year int) string {
    weekdays := [...]string{
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
    }
    monthDays := [...]int{
        31,
        28,
        31,
        30,
        31,
        30,
        31,
        31,
        30,
        31,
        30,
        31,
    }
    lastYear := year - 1
    leapYears := lastYear/4 - lastYear/100 + lastYear/400 - (1970/4 - 1970/100 + 1970/400)
    days := (year-1971)*365 + leapYears + day - 1

    for m := 1; m < month; m++ {
        days += monthDays[m-1]
    }
    if month > 2 && (year%400 == 0 || (year%4 == 0 && year%100 != 0)) {
        days++
    }
    return weekdays[(days+5)%7]
}

复杂度分析

  • 时间复杂度:$O(1)$。完整年份用公式一次计算,月份循环最多执行十一次。
  • 空间复杂度:$O(1)$。星期表与月份表的大小均固定。

关键点总结

[!green]

  • 从已知星期的基准日起累计经过天数,再对七取模。
  • 整年闰日只统计到目标年前一年,本年闰日按目标月份另外处理。
  • 基准当天偏移为零,所以本月贡献为 day - 1。

解法对比

逐年累加也能求出经过天数,但需要逐年判断闰年。这里用整除公式直接统计完整年份的闰日,只保留最多十一次的月份累加,仍能清楚区分年、月、日三部分。

易错点总结

[!yellow]

  • 只判断年份能否被四整除,会把不能被四百整除的整百年份误算为闰年。
  • 整年计数统计到目标年,会与本年的月份累计重复。
  • 闰年一月、二月不能统一加一天,闰日此时尚未完整经过。
  • 使用 day 而非 day - 1,或忘记基准星期五的偏移五,都会使结果错位。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/92155548
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!