题目重点:按题目的 25%/75% 阈值分类每次观看,再按账户求主导观看模式并汇总人数;包含整数边界、平局约定与独立验证代码。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 观看比例使用 watched_secs/title_runtime_secs;runtime 必须大于零,watched 在 [0,runtime],不满足则训练代码报错。题目未明确异常输入处理规则。
- 25% 与 75% 本身归 Sampled;低于 25% 为 Abandoned,高于 75% 为 Completed。
- 若某账户出现次数最高的类别并列,训练版固定按 Abandoned、Sampled、Completed 的顺序选首个;真实面试应向面试官确认平局策略。day_nbr 和 title_id 在已公开三项输出中无需使用。
解题路线
- 单次扫描每个 session。比较 4×watched 与 runtime、3×runtime,避免 0.25/0.75 的浮点边界误差。
- 按 account_id 维护三个类别计数。对每个账户选最高频类别,并把该用户计入结果中的一个桶;这里统计的是用户数,不是 session 数。
- 用固定类别顺序让平局输出可复现;若题目改为返回所有并列类别或最近一次类别,只需替换每用户归约步骤,不能悄悄沿用此训练约定。
手动走一遍
题目 U1 的三次观看均超过 75%,归 Completed;U4 看了 2000/3600,处于 25%–75%,归 Sampled。训练例一个用户分别有一次 Abandoned 和一次 Completed 时,因平局约定归 Abandoned。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from collections import Counter, defaultdict
PATTERNS = ("Abandoned", "Sampled", "Completed")
def analyze_engagement_patterns(viewing_data):
"""Training tie rule: Abandoned, then Sampled, then Completed."""
per_account = defaultdict(Counter)
for session in viewing_data:
runtime = session["title_runtime_secs"]
watched = session["watched_secs"]
if runtime <= 0 or watched < 0 or watched > runtime:
raise ValueError("invalid viewing duration")
if 4 * watched < runtime:
pattern = "Abandoned"
elif 4 * watched <= 3 * runtime:
pattern = "Sampled"
else:
pattern = "Completed"
per_account[session["account_id"]][pattern] += 1
result = {pattern: 0 for pattern in PATTERNS}
for counts in per_account.values():
dominant = max(PATTERNS, key=lambda pattern: counts[pattern])
result[dominant] += 1
return result
复杂度
N 条 session、U 个账户,扫描 O(N),每账户固定三个桶归约 O(U),总时间 O(N+U),空间 O(U)。若只需聚合类别计数,不需要保存每条 session。
易错点与追问
容易失分的边界
- 把 25% 误判为 Abandoned 或把 75% 误判为 Completed。
- 按 title_id 或 day_nbr 去重,但题目没有要求合并跨日观看。
- 对 session 直接计数当成用户数。
- 没有说明最高频并列时的返回规则。
- 零片长时直接除法,或用浮点近似导致边界不稳定。
继续深挖
- 如果同一 account/title 跨天多次观看,何时应该合并为一次内容体验?
- 若数据按日流入、用户类别可能改变,怎样维护增量计数?
- 如何按用户分组输出占比,同时避免一名重度用户主导 session 统计?
