满足条件的连续 N 行
经典的“连续三天销售额超过 X”窗口模式。
满足条件的连续 N 行 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
LeetCode 经典题
这是 SQL 面试中最常被问到的问题之一:“找出销售额超过某个阈值的连续三天中的所有日期,” 或者 LeetCode 中广受欢迎的题目:“报告出席人数连续 3 行及以上超过 100 的体育场记录。”
其结构始终相同:只有当某行位于由 N 个连续的符合条件的行组成的连续段中时,该行才符合条件。本课将介绍两种清晰的解决方案,以及最容易让候选人掉入的陷阱。
示例数据
我们使用一个每日 sales 表。条件是 amount > 100。我们必须返回属于一个连续 3 天或以上、且每天都满足该条件的连续段的每一天。
sale_date— 每天一行amount— 当天的销售总额
关键细节是:行必须在序列中连续;对于基于日期的版本,它们在日历中也必须连续。
SELECT * FROM sales ORDER BY sale_date;
-- sale_date | amount
-- 2024-03-01 | 120
-- 2024-03-02 | 150
-- 2024-03-03 | 130
-- 2024-03-04 | 90
-- 2024-03-05 | 200方法一:先筛选,再划分连续区段
稳健的方法是:先只保留符合条件的行,然后将保留下来的行分组成连续区段,最后只保留长度至少为 N 的连续区段。
第一步是使用 WHERE 进行筛选。第二步复用间隔与连续区段方法中的锚点。由于我们先进行了筛选,这里的连续区段就表示“由连续的符合条件的日期组成的一段记录”。
WITH qualifying AS (
SELECT sale_date
FROM sales
WHERE amount > 100
)
SELECT * FROM qualifying ORDER BY sale_date;为符合条件的连续段建立锚点
按日期为符合条件的行编号,然后通过相减得到连续区段锚点。在日历上连续 AND 全部符合条件的行会共享同一个锚点;任何不符合条件的日期都会被移除,从而准确地在应断开的地方切断连续段。
WITH qualifying AS (
SELECT sale_date
FROM sales
WHERE amount > 100
),
numbered AS (
SELECT sale_date,
ROW_NUMBER() OVER (ORDER BY sale_date) AS rn
FROM qualifying
)
SELECT sale_date, sale_date - rn AS grp
FROM numbered;保留长度足够的连续区段
按锚点分组,统计行数,只保留包含 COUNT(*) >= 3 的组。如果面试官还想要返回单独的符合条件的日期,就将保留下来的锚点与已编号的行连接起来。
WITH qualifying AS (
SELECT sale_date FROM sales WHERE amount > 100
),
numbered AS (
SELECT sale_date,
ROW_NUMBER() OVER (ORDER BY sale_date) AS rn
FROM qualifying
),
islands AS (
SELECT sale_date - rn AS grp, COUNT(*) AS len
FROM numbered
GROUP BY sale_date - rn
HAVING COUNT(*) >= 3
)
SELECT n.sale_date
FROM numbered n
JOIN islands i ON n.sale_date - n.rn = i.grp
ORDER BY n.sale_date;方法二:滑动 COUNT 窗口
当 N 较小且固定时,可以采用更简洁的方法:使用窗口框架统计相邻行中有多少行同样符合条件。只要包含当前行的某个 N 行连续窗口全部符合条件,该行就属于结果。
首先添加一个布尔标记,然后在滑动窗口框架上对该标记求和。
SELECT sale_date, amount,
CASE WHEN amount > 100 THEN 1 ELSE 0 END AS ok
FROM sales;对三个窗口框架求和
对于长度为 3 的连续段,如果以此行为结束、以此行为中心或从此行开始的 3 行窗口的总和为 3,则该符合条件的行属于结果。计算这三个滚动总和,并检查其中是否有一个等于 3。
LeetCode 601(体育场的人流量)解决方案背后使用的就是这一技巧。
WITH flagged AS (
SELECT sale_date, amount,
CASE WHEN amount > 100 THEN 1 ELSE 0 END AS ok
FROM sales
),
w AS (
SELECT *,
SUM(ok) OVER (ORDER BY sale_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS s_end,
SUM(ok) OVER (ORDER BY sale_date
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS s_mid,
SUM(ok) OVER (ORDER BY sale_date
ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS s_start
FROM flagged
)
SELECT sale_date, amount
FROM w
WHERE ok = 1 AND (s_end = 3 OR s_mid = 3 OR s_start = 3);日历间隔陷阱
窗口求和方法使用 ROWS,它统计的是相邻的结果行,而不是相邻的日历日期。如果某个不符合条件的日期已经被过滤掉,结果中的两行虽然彼此相邻,却可能并不在日历上连续。
要点:将滑动窗口应用于完整的每日序列(不要预先筛选),或者使用能够天然遵循日历间隔的日期锚点方法。在面试中说明这一取舍。
推广到任意 N
方法一(先筛选再划分连续区段)可以直接推广:只需修改 HAVING COUNT(*) >= N。这是它相较于多窗口求和的一大优势,后者会随着 N 增大而需要更多窗口框架。
对于参数化的 N 或较大的 N,应优先选择连续区段方法 — 它只需修改一次阈值,而不是手写 N−1 个窗口。
-- only the threshold changes for N = 5
HAVING COUNT(*) >= 5选择方法
可以直接口头说明的快速判断指南:
- 先筛选再划分连续区段:遵循日历间隔,可推广到任意 N,并返回完整的连续段 — 这是安全的默认选择。
- 滑动窗口求和:适用于稠密每日序列中的固定小 N,写法简洁,但要注意 ROWS 与日历之间的陷阱。
同时说出两种方法,再说明选择理由,正是中高级面试官看重的表现。
完整解决方案
以下答案可移植、适用于任意 N、遵循日历连续性,并返回符合条件的日期:
WITH qualifying AS (
SELECT sale_date FROM sales WHERE amount > 100
),
numbered AS (
SELECT sale_date,
ROW_NUMBER() OVER (ORDER BY sale_date) AS rn
FROM qualifying
),
islands AS (
SELECT sale_date - rn AS grp, COUNT(*) AS len
FROM numbered
GROUP BY sale_date - rn
HAVING COUNT(*) >= 3
)
SELECT n.sale_date
FROM numbered n
JOIN islands i ON n.sale_date - n.rn = i.grp
ORDER BY n.sale_date;快速检查
找出其中隐蔽的错误。
回顾
对于满足某个条件的 N 个连续行:
- 先筛选再划分连续区段:保留符合条件的行,使用
date - ROW_NUMBER()建立锚点,分组,并使用HAVING COUNT(*) >= N。这种方法可推广,并且遵循日历间隔。 - 滑动窗口求和:为行添加标记,在固定的 N 行窗口框架上求和;写法简洁,但对于预先筛选的数据,要注意 ROWS 与日历之间的差异。
接下来:计算截至今天用户当前的活跃连续天数。
常见问题解答
「满足条件的连续 N 行」课时是免费的吗?
是的 — 「满足条件的连续 N 行」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「满足条件的连续 N 行」这节课中我会学到什么?
经典的“连续三天销售额超过 X”窗口模式。 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「满足条件的连续 N 行」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 检测连续日历日
- 每位用户的最长连续记录
- 满足条件的连续 N 行
- 截至今天的当前连续记录