体育馆人流高峰:连续三天以上人数不少于 100
LeetCode 601 · 连续区间、LAG/LEAD、行号差分组
面试怎么回答
我会先过滤出 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 连续;不连续时用行号差分组法更稳。