订阅更新

体育馆人流高峰:连续三天以上人数不少于 100

SQL 实战 123 题 · 第 32 题 困难 约 3 分钟读完

LeetCode 601 · 连续区间、LAG/LEAD、行号差分组

本文目录
  1. 面试怎么回答
  2. 题意
  3. 表结构
  4. 思路
  5. 参考解法
  6. 易错点

面试怎么回答

我会先过滤出 people >= 100 的行,再用 id - ROW_NUMBER() OVER (ORDER BY id) 作为连续段编号:同一段里这个差值相同。按段计数,长度大于等于 3 的段整段输出。这个「行号差」套路能解决所有连续 N 天、连续登录类的问题。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

题意

体育馆每天一条记录(编号、日期、人数),编号随日期递增。找出所有「连续三条及以上记录的人数都不少于 100」的记录,按日期输出。

表结构

  • stadium:id(主键)、visit_date、people

思路

一条记录属于高峰,只要它在某个长度为 3 的连续窗口里:它是窗口的第一、第二或第三条。原解法用 LAG/LEAD 拿到前后两行,三种位置任意一种成立就保留。更通用的做法是先筛出人数 ≥ 100 的行,用「id 减去行号」得到连续段编号,段内行数 ≥ 3 的整段输出。

参考解法

写法 1:三表自连接,枚举三种相对位置

select distinct t1.*
from stadium t1, stadium t2, stadium t3
where t1.people >= 100 and t2.people >= 100 and t3.people >= 100
and
(
        (t1.id - t2.id = 1 and t1.id - t3.id = 2 and t2.id - t3.id =1)  -- t1, t2, t3
    or
    (t2.id - t1.id = 1 and t2.id - t3.id = 2 and t1.id - t3.id =1) -- t2, t1, t3
    or
    (t3.id - t2.id = 1 and t2.id - t1.id =1 and t3.id - t1.id = 2) -- t3, t2, t1
)
order by t1.id

写法 2:LAG / LEAD 取前后两行

select id,visit_date,people from
(
    select id
    ,lead(people,1) over(order by id) ld
    ,lead(people,2) over(order by id) ld2
    ,visit_date
    ,lag(people,1) over(order by id) lg
    ,lag(people,2) over(order by id) lg2
    ,people
    from stadium
    ) a
where (a.ld>=100 and a.lg>=100 and a.people>=100)
or (a.ld>=100 and a.ld2>=100 and a.people>=100)
or (a.lg>=100 and a.lg2>=100 and a.people>=100)

另一种写法:行号差分组:适合任意「连续 N 条」的问题

SELECT id, visit_date, people
FROM (
  SELECT *, COUNT(*) OVER (PARTITION BY grp) AS len
  FROM (
    SELECT *, id - ROW_NUMBER() OVER (ORDER BY id) AS grp
    FROM stadium
    WHERE people >= 100
  ) a
) b
WHERE len >= 3
ORDER BY visit_date

易错点

  • 只判断「自己和后两行」会漏掉高峰段的最后两条记录。
  • LAG/LEAD 按 id 取相邻行,前提是 id 连续;不连续时用行号差分组法更稳。
刷题进度保存在当前浏览器