ZipRecruiter 2026 New Grad OA 不卷算法,但极度卷设计——一道大题分 4 个递进 Level:CRUD、Scan & Prefix、TTL 过期清理、Backup / Restore 快照。每个 Level 单独看都不难,但4 个 Level 必须共享一套数据结构——前面设计的偷懒会在后面付出代价。本文给出每个 Level 的最优数据结构、Python 完整解法与 ZipRecruiter codesignal 评分系统的隐藏 case 防御。
ZipRecruiter NG OA 概览
| 维度 | 详情 |
|---|---|
| 平台 | CodeSignal Test |
| 时长 | 90 分钟 |
| 题量 | 1 道大题,4 个 Level |
| 难度 | LeetCode Easy~Medium,重设计 |
| 通过线 | 80% 测试通过 |
| 评分维度 | 正确性 + Level 完成度 |
题目核心:In-memory Database
实现一个内存键值数据库,支持嵌套结构:
db[key][field] = value
key 是顶层名称,field 是 key 下的字段名。所有操作要么对 key 整体作用,要么对 (key, field) 作用。
Level 1:基础 CRUD
操作:
set(key, field, value):设置字段值get(key, field):取值,不存在返回Nonedelete(key, field):删除字段,返回是否成功
class Database:
def __init__(self):
self.db: dict = {}
def set(self, key, field, value):
self.db.setdefault(key, {})[field] = value
def get(self, key, field):
return self.db.get(key, {}).get(field)
def delete(self, key, field):
if key in self.db and field in self.db[key]:
del self.db[key][field]
if not self.db[key]:
del self.db[key]
return True
return False
Level 1 关键:
- delete 返回
True/False,不要抛异常 - key 下所有 field 都被删后,把 key 也清掉——否则 Level 2 的 scan 会返回空 key
Level 2:Scan & Prefix Filtering
操作:
scan(key):返回 key 下所有"field(value)"字符串,按字典序排序scan_by_prefix(key, prefix):仅返回 field 以 prefix 开头的
def scan(self, key):
if key not in self.db:
return []
return [f"{f}({v})" for f, v in sorted(self.db[key].items())]
def scan_by_prefix(self, key, prefix):
if key not in self.db:
return []
return [
f"{f}({v})"
for f, v in sorted(self.db[key].items())
if f.startswith(prefix)
]
Level 2 隐藏 case:
- key 不存在:返回
[](不是None,不是异常) - prefix 为空字符串:返回所有字段(等价于 scan)
- prefix 大小写敏感
Level 3:TTL(Time-to-Live)
操作:
set_at(key, field, value, timestamp):带时间戳的 setset_at_with_ttl(key, field, value, timestamp, ttl):设置 TTL,超过timestamp + ttl则失效get_at(key, field, timestamp):在某时间点取值,过期返回Nonedelete_at(key, field, timestamp):在某时间点删除scan_at(key, timestamp)/scan_by_prefix_at(key, prefix, timestamp):带时间戳的 scan
核心设计:每个字段存 (value, start, ttl) 三元组,ttl=None 表示永久。
def __init__(self):
self.db: dict = {}
def _is_alive(self, entry, timestamp):
value, start, ttl = entry
if ttl is None:
return True
return timestamp < start + ttl
def set_at(self, key, field, value, timestamp):
self.db.setdefault(key, {})[field] = (value, timestamp, None)
return ""
def set_at_with_ttl(self, key, field, value, timestamp, ttl):
self.db.setdefault(key, {})[field] = (value, timestamp, ttl)
return ""
def get_at(self, key, field, timestamp):
if key not in self.db or field not in self.db[key]:
return None
entry = self.db[key][field]
if not self._is_alive(entry, timestamp):
return None
return entry[0]
def delete_at(self, key, field, timestamp):
if key not in self.db or field not in self.db[key]:
return False
entry = self.db[key][field]
if not self._is_alive(entry, timestamp):
return False
del self.db[key][field]
if not self.db[key]:
del self.db[key]
return True
def scan_at(self, key, timestamp):
if key not in self.db:
return []
alive = [
(f, e[0]) for f, e in self.db[key].items()
if self._is_alive(e, timestamp)
]
return [f"{f}({v})" for f, v in sorted(alive)]
Level 3 三个高频扣分点:
- scan 不能清理过期字段 —— 只过滤不删除,否则会破坏后续 set_at_with_ttl 的语义
- delete_at 对过期字段返回 False —— 已过期等价于"不存在"
- TTL 是
start + ttl严格小于的判断 ——timestamp < start + ttl而不是<=
Level 4:Backup / Restore
操作:
backup(timestamp):把当前数据库状态快照保存restore(timestamp, target_timestamp):恢复到target_timestamp 之前最近一次 backup的状态;恢复时所有字段的 ttl 按 target_timestamp 到当前 timestamp 的差****重新计算剩余 TTL
def __init__(self):
self.db: dict = {}
self.backups: list = [] # [(backup_ts, snapshot)]
def _snapshot(self, timestamp):
snap = {}
for key, fields in self.db.items():
snap_fields = {}
for f, (v, start, ttl) in fields.items():
if ttl is None:
snap_fields[f] = (v, None, None)
continue
remaining = start + ttl - timestamp
if remaining > 0:
snap_fields[f] = (v, timestamp, remaining)
if snap_fields:
snap[key] = snap_fields
return snap
def backup(self, timestamp):
self.backups.append((timestamp, self._snapshot(timestamp)))
return str(sum(len(fs) for fs in self.backups[-1][1].values()))
def restore(self, timestamp, target_timestamp):
candidate = None
for bk_ts, snap in self.backups:
if bk_ts <= target_timestamp:
candidate = (bk_ts, snap)
if candidate is None:
self.db = {}
return ""
bk_ts, snap = candidate
# Re-base TTL to the current timestamp
self.db = {}
for key, fields in snap.items():
for f, (v, start, ttl) in fields.items():
if ttl is None:
self.db.setdefault(key, {})[f] = (v, timestamp, None)
else:
self.db.setdefault(key, {})[f] = (v, timestamp, ttl)
return ""
Level 4 关键设计:
- snapshot 时只保存仍存活的字段——过期的不能 restore 回来
- restore 时所有 TTL 以 restore 时间为锚点重新计算——这是 ZipRecruiter 这一 Level 的"狠点"
- 找不到合适 backup 时,清空数据库而非保留当前状态
ZipRecruiter OA 通关策略
1)Level 优先级:1 → 2 → 3 → 4
每个 Level 占总分约 25%。Level 1+2 写到 100% = 50% 保底,足以进入 CodeSignal 的 Top 30% 分位。Level 3 是难度突变点——TTL 设计错就 Level 3 全挂。Level 4 是 stretch goal。
2)数据结构一次到位
不要 Level 1 用 db[key][field] = value,Level 3 改成 (value, start, ttl)——会逼你重写 Level 1+2 的所有代码。Level 1 就用 tuple,永久字段用 (value, 0, None)。
3)函数返回值类型一致
CodeSignal 评分对字符串 / None / boolean / "" 返回值类型敏感。题面要求返回什么就严格返回什么——get_at 返回 None,set_at 返回 "",delete_at 返回 boolean。
FAQ
ZipRecruiter OA 通过率高吗?
整体约 40-50%。Level 1+2 完成度高的候选人通过率 70%+,Level 3 是淘汰主战场——TTL 语义稍有偏差会扣 25% 分。
CodeSignal Test 有调试环境吗?
有。CodeSignal 提供 stdout 打印 + 自定义测试 case 运行。强烈建议每完成一个 Level 手动跑 3-5 个边界 case:空 key、空 prefix、TTL 刚好过期、backup 后再 delete。
4 个 Level 都做完需要多久?
熟练 candidate 约 60-75 分钟。前 10 分钟规划 → 20 分钟 Level 1+2 → 30 分钟 Level 3 → 15 分钟 Level 4。Level 4 来不及的话保住 Level 3 满分也能进 final round。
这道题的"亲戚"是什么?
CodeSignal 同款数据库题:Snowflake、Optiver、Capital One、Roku 都有变体。前缀 + TTL 几乎是 CodeSignal 平台的标准模板,题面只换"数据库 → 仓库 → 用户系统"。
ZipRecruiter OA 之后会有什么轮次?
OA 通过后进入 1 轮 HR Phone Screen,再到 2-3 轮 Technical Onsite(含 1 轮 Behavioral + 1-2 轮 Coding + 1 轮 System Design for senior 岗位)。从 OA 到 onsite 平均 2-4 周。
正在准备 ZipRecruiter NG OA?
oavoservice 提供 CodeSignal 内存数据库题型全流程辅助:Level 拆解、TTL 设计模板、Backup / Restore 快照算法。我们对 ZipRecruiter、Snowflake、Optiver 等 CodeSignal 系列公司的 4-Level 设计题有完整复盘,可以根据你的目标公司客制 OA 演练。
立即添加微信:Coding0201,获取 ZipRecruiter OA 一对一辅导。
#ZipRecruiter #ZipRecruiterOA #NewGrad #CodeSignal #内存数据库 #OA真题
联系方式
Email: [email protected]
Telegram: @OAVOProxy