题目难度: 中等

原题链接

今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

给定 N 个人的出生年份和死亡年份,第 i 个人的出生年份为 birth[i],死亡年份为 death[i],实现一个方法以计算生存人数最多的年份。

你可以假设所有人都出生于 1900 年至 2000 年(含 1900 和 2000 )之间。如果一个人在某一年的任意时期处于生存状态,那么他应该被纳入那一年的统计中。例如,生于 1908 年、死于 1909 年的人应当被列入 1908 年和 1909 年的计数。

如果有多个年份生存人数相同且均为最大值,输出其中最小的年份。

示例:

  • 输入:
    • birth = {1900, 1901, 1950}
    • death = {1948, 1951, 2000}
  • 输出: 1901

提示:

  • 0 < birth.length == death.length <= 10000
  • birth[i] <= death[i]

题目思考

  1. 如何做到线性时间复杂度?

解决方案

思路

  • 分析题目, 一个很容易想到的思路就是暴力法, 外层遍历 1900 到 2000 的每个年份, 然后内层遍历每个人, 统计当前年份仍然生存的人数
  • 但这种方法效率很低, 时间复杂度达到了 O(N*Y) (N 是总人数, Y 是总年份), 有没有更高效的方法呢?
  • 重新思考问题, 针对每个人的出生时间 b 和死亡时间 d, 其本质就是将年份区间[b,d]的生存人数增加 1, 更新完所有年份区间后, 我们只需要从小到大遍历年份, 即可快速得到当前年份的生存人数了
  • 但是, 直接更新区间的时间复杂度是 O(Y), 如何优化这部分呢?
  • 这里我们就可以使用差分数组+前缀和的思路
    • 差分数组统计当前年份与前一年份的生存人数之差, 即diff[y]=people[y]-people[y-1], 注意由于年份 0 没有前一年份, 所以diff[0]=people[0]
    • 而如何求当前年份 y 的生存人数呢? 由于diff[y]=people[y]-people[y-1], 我们只需要累加 0~y 的所有 diff, 即可得到 people[y]
  • 这样一来, 更新[b,d]区间就只需要更新两个端点了, 因为相当于年份 b 增加了一个生存人数, 而年份 d+1 减少了一个生存人数, 即diff[b]++, diff[d+1]--, 将更新区间的时间复杂度降为 O(1)
  • 另外注意题目的年份范围在 19002000 之间, 所以我们无需存储 02000 所有年份, 而是可以将年份减去 1900, 这样只需要存储 0~100 即可, 进一步优化了空间消耗
  • 下面代码就对应了上面的整个过程, 且每一步有详细的注释, 方便大家理解

复杂度

  • 时间复杂度 O(N+Y): 只需要遍历一遍 N (总人数, 110000), 再遍历一遍 Y (总年份, 19002000)
  • 空间复杂度 O(Y): 只需要存储所有年份的差分数组

代码

class Solution:
    def maxAliveYear(self, birth: List[int], death: List[int]) -> int:
        # 差分数组+下标转换
        n = len(birth)
        # diff是差分数组, 统计区间两个端点的变化情况
        # 注意diff下标最大可以到101(对应死亡时间2000年的情况), 所以这里数组上限是102
        diff = [0] * 102
        for i in range(n):
            # 出生年份的人数加1
            # 这里将年份减去1900, 转成0~100的值, 减少空间消耗
            diff[birth[i] - 1900] += 1
            # 死亡+1年份的人数减1
            diff[death[i] + 1 - 1900] -= 1
        sm = 0
        mx, res = 0, 0
        for i, x in enumerate(diff):
            # sm是前缀和, 即为当前年份的生存人数
            sm += x
            if sm > mx:
                mx = sm
                # 注意转换成原始年份
                res = i + 1900
        return res

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我