题目重点:从 Data Platform VO 的 score update 和无重复 rotation 出发,给出可验证的简单实现,并推导两个有序集合支持高效更新与多线程。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版电影集合固定,分数可更新,get/next 选本轮尚未展示电影中的最高分,同分按 movie id 稳定排序;题目只说支持 score update 与 rotation,没有给排序和同分规则。
- 更新已展示电影的分数只影响下一轮;更新未展示电影的分数影响本轮后续选择。空集合返回 None。
- 本地单进程版本不支持 add/remove;若要动态增删,必须约定新片在当前轮还是下轮加入。
解题路线
- 维护 scores 与 shown。每次 next 从 `scores−shown` 选择最高分,返回后标记 shown;当 shown 覆盖全部电影时清空,开启下一轮。参考代码用线性扫描先保证语义容易验证。
- 生产优化维护两个按 `(score,id)` 有序的结构:available 存本轮未展示,shown 存已展示。next 从 available 取最高并移到 shown;更新分数时在对应结构中删除旧键并插入新键;available 为空时交换两结构开启下一轮。平衡树或 TreeMap/SortedList 支持 O(log M) 操作,单纯 heap 对任意 score update 要版本化惰性删除并注意内存增长。
- 多线程下 next 与轮回切换、score 更新必须在同一一致性边界内;可先用全局 mutex 保正确,再用分区/单 owner actor 处理吞吐。分布式持久化需保存两集合状态和分数版本,重复请求要幂等。
手动走一遍
训练例 A:9、B:5、C:2,依次 next 得 A、B;此时把 C 改为 10,下次仍得 C,因为本轮只剩 C。再 next 时新一轮开始,C 以 10 分最先出现。同分用 id 排序只属训练约定。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
class MovieRotation:
"""Simple reference: highest score among unseen movies, stable ID tie-break."""
def __init__(self, scores):
self.scores = dict(scores)
self.shown = set()
def update_score(self, movie_id, score):
if movie_id not in self.scores:
raise KeyError(movie_id)
self.scores[movie_id] = score
def next_movie(self):
if not self.scores:
return None
if len(self.shown) == len(self.scores):
self.shown.clear()
eligible = (movie_id for movie_id in self.scores if movie_id not in self.shown)
movie_id = min(eligible, key=lambda item: (-self.scores[item], item))
self.shown.add(movie_id)
return movie_id
复杂度
参考代码每次 next 扫描 M 部电影 O(M),update O(1),存储 O(M)。两个平衡有序集合的 next 与 update O(log M),轮次切换可交换引用 O(1);若需要复制不可变快照或分布式事务,成本更高。
易错点与追问
容易失分的边界
- 每次直接从全体最高分取片,导致高分电影重复霸屏。
- 更新已展示片后立即让其再次返回,违反每轮一次。
- 只使用一个 heap,无法简单做 score update 与已展示集合的循环切换。
- 同分没有稳定规则,多线程请求顺序难复现。
- 把 的外链题面细节直接当作 的完整同题证据。
继续深挖
- 多线程同时请求最后一部片时如何只触发一次轮次切换?
- 动态加入新片时插入当前轮还是下一轮,用户体验有何差别?
- 分布式多个展示节点共享轮播状态,如何平衡严格无重复与延迟?
