题目重点:Tech screen 题目给整数序列 RadixCache.insert 和两组树形示例;按最长公共前缀讨论完整命中、前缀终止与中途分叉。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版根节点边为空,每条边标签为非空整数数组,孩子的首个整数互异;节点 terminal 标记一条插入序列在此结束,题目未明确终止标记接口。
- 插入相同序列幂等,允许一个已插入序列是另一个的前缀;只讨论 insert,不推断 eviction、KV-cache payload 或最长前缀查询 API。
- 整数可重复、可为负数;前缀比较逐整数而非字符串化字符。
解题路线
- 从根开始,用下一个整数定位唯一候选子边;若无候选,就把剩余后缀作为一条新边并标记终端。
- 设待插后缀 S、候选边 E 的最长公共前缀长度为 c。若 c=|E|,沿该边向下,消耗 c;S 也耗尽时标记现节点 terminal。
- 若 0<c<|E|,把旧边拆成公共前缀 P 的新中间节点和旧后缀 E[c:];再把 S[c:] 接为另一条新边。若 S[c:] 为空,中间节点自身 terminal;原旧子树必须挂在旧后缀下而不可丢失。
- 同一节点孩子用首整数 hash map,找候选平均 O(1),每次比较相应边标签;若按显示树形输出,则排序孩子只影响呈现顺序,不影响插入正确性。
- 用参考集合独立校验:遍历终端节点并串联路径边,所得序列集合必须等于已插入的 distinct 输入集合;同时检查每条边非空及兄弟边首整数唯一。
手动走一遍
先插 [1,2,3,4,5,6],再插 [1,2,3,40,50,60,70,80]:公共前缀 [1,2,3] 成中间节点,两条后缀分别为 [4,5,6]、[40,50,60,70,80]。再插 [1,2,3,40,50,60,700,800],后两者公共前缀 [40,50,60] 成第二个中间节点,分支 [70,80] 与 [700,800];70 和 700 是不同整数,不共享数字字符‘70’。
规模与取舍
长度为 L 的新序列,沿途比较的元素总量至多 O(L),新边与拆边复制的标签元素总量 O(L)(共享视图需另算寿命)。存 N 条总长 M 的序列,最坏空间 O(M);只存共同前缀一次时通常更省。
易错点与追问
容易失分的边界
- 用字符串 startsWith 比较整数 70 与 700,得到错误公共前缀。
- 插入较短前缀时仅保留中间节点而没标 terminal。
- 拆旧边后忘记把旧 child/subtree 接回,先前序列消失。
- 把示例孤立的 [1,7,6] 当成已明确要求的第三个 milestone。
继续深挖
- 如何实现 longest-prefix lookup 并返回终端节点的 payload?
- 删除一条序列后何时可把单孩子节点重新压缩?
- 并发插入共享路径时如何锁定/复制节点?
- 若边标签与外部 KV cache 页面绑定,拆分时怎样处理引用计数?
