
这轮 Uber SDE2 Level VO 的 coding / design 环节,题目本身不难,但非常考察候选人对“动态状态 + 顺序约束”的建模能力。
题目要求实现一个服务,支持两个接口:
postCustomerVisit(customerId):记录一次访问getFirstOneTimeVisitor():返回当前“只访问过一次”且最早出现的用户
如果没有符合条件的人,返回 None / null。
题目真正考什么
这题的重点不是复杂算法,而是同时维护两件事:
- 动态更新:用户访问会持续进入,状态不断变化
- 稳定顺序:返回的不是任意 one-time 用户,而是最早出现的那个
很多人会卡在“如何在用户第二次访问时把他从顺序结构里删掉”。如果你选的是队列,中间删除并不高效。
面试里最稳的建模方式
两个数据结构就够:
HashMap:记录每个customerId的访问次数Queue:按第一次出现顺序入队
更新逻辑
调用 postCustomerVisit(customerId) 时:
- 访问次数加一
- 如果这是第一次出现,放到队尾
查询逻辑
调用 getFirstOneTimeVisitor() 时:
- 看队首用户当前计数是否仍然是 1
- 如果不是 1,说明已失效,弹出队首
- 重复直到队首有效或队列为空
- 队首有效即答案;队空返回
None
为什么“懒删除”是关键点
队列中会存在“过期用户”:第一次访问时进队,后续访问后已不再 one-time。
这不是 bug,而是设计选择。我们不在第二次访问时去队列中间删除,而是在查询时从队首清理失效元素。这个模式在流式数据场景里非常常见,也更容易讲清复杂度。
面试官常见追问
1)复杂度是多少
postCustomerVisit:均摊 O(1)getFirstOneTimeVisitor:均摊 O(1)
虽然查询里可能连续弹多个元素,但每个元素最多被弹一次。
2)并发场景怎么处理
SDE2 层级通常会追问线程安全。可从两个方向回答:
- 单机:用锁保护 map + queue 的一致更新
- 分布式:按
customerId分片,避免跨分片顺序冲突
3)内存增长怎么控制
如果长期运行,历史 ID 会很多。可以补充:
- 引入 TTL 或时间窗口
- 只维护最近 N 天访问数据
- 将冷数据落盘,热数据留内存
Uber SDE2 VO 常见 3 个 BQ 问题
- 讲一次你在项目里发现方向不对并主动纠偏的经历。你怎么识别风险、怎么推动团队接受新方案?
- 讲一次你和 PM / 跨团队对优先级有分歧的经历。你如何平衡业务时效和工程质量?
- 讲一次线上故障处理经历。你当时如何拆分问题、沟通同步、以及后续如何防止同类问题再次发生?
这些 BQ 本质上都在看 SDE2 的 owner 意识:不是“完成任务”,而是“把结果和影响负责到底”。
备考建议
这类题建议按这条线练习:
- 先讲清状态定义
- 再讲更新和查询路径
- 最后讲复杂度与失效元素处理
面试里把这个顺序讲顺,往往比一上来写代码更加分。
需要面试真题? 立刻联系微信 Coding0201,获取真题。
联系方式
Email: [email protected]
Telegram: @OAVOProxy