北美软件工程师面经 2026:候选人在白板前讲解系统设计与算法题

Optiver SquirrelResearch 面试题2026:多层藏取系统深度解析

SEO Title: Optiver SquirrelResearch 面试题2026:OA 难题全流程拆解与高分策略

Optiver SquirrelResearch 面试题2026:多层藏取系统深度解析

Optiver SquirrelResearch 面试题2026 是 2026 年最新高压 OA 题。
因此,这题不能只靠模板记忆。
你必须先建清晰状态机。
这是我们学员贡献的最新面经。

此外,这题同时考实现和稳定性。
你要处理初始化、入库、出库。
与此同时,你还要处理过期和回填。
换句话说,这是一道规则工程题。

2026 面试流程深度复盘:Optiver SquirrelResearch 面试题2026

首先,线上环节会先给一段长规则。
但是,很多人会漏掉失败条件。
因此,第一步是先画失败清单。
Optiver SquirrelResearch 面试题2026 常在此处拉开差距。

其次,编码一般分三段推进。
先写 init(locations)
随后写 HideNut 的合法性校验。
最后写 RetrieveNuts 的取出策略。

此外,时间语义是核心扣分点。
timestamp + TTL 到点即过期。
因此,取出前必须先清过期。
过期删除后,nut_id 必须可复用。

与此同时,部分场次有反应力测试。
典型形式是 zap n 即时操作。
因此,手速和心态都被考察。
总而言之,Optiver SquirrelResearch 面试题2026 不只考算法。

核心题目解析

具体来说,系统可拆成三层状态。
全局层管时间和 nut_id 唯一性。
地点层管多层容量。
层结构管排序和回填。

因此,容量建议用斐波那契扩展。
三层可设为 1/2/3
四层可扩为 1/2/3/5
Optiver SquirrelResearch 面试题2026 常追问这里。

此外,规则可以归纳为以下不变量。
- init(locations):按地点初始化层数与容量。
- HideNut:非法地点、重复 ID、容量满都返回 False
- HideNut:按“由深到浅”找首个可用层写入。
- RetrieveNuts:非法地点或空地点返回空列表。
- RetrieveNuts:先算可达层,再做“更重优先”。
- RetrieveNuts:同重按 nut_id 字典序。
- 访问下一层条件:上一层占用率 < 50%
- 非顶层取走后:上层最轻坚果下落补位。

System Design 流程图

flowchart TD
A[请求到达] --> B{timestamp 严格递增?}
B -- 否 --> X[失败返回]
B -- 是 --> C[清理过期坚果]
C --> D{HideNut / RetrieveNuts}
D -- HideNut --> E{地点合法 + ID唯一 + 有容量?}
E -- 否 --> X
E -- 是 --> F[按深到浅写入]
F --> G[成功]
D -- RetrieveNuts --> H{地点合法且非空?}
H -- 否 --> R[返回空列表]
H -- 是 --> I[计算可达层]
I --> J[跨层选最重; 同重按ID]
J --> K[删除并触发跨层回填]
K --> L{达到携带上限?}
L -- 否 --> I
L -- 是 --> M[返回结果列表]

Python 参考实现(可直接改造成面试答案)

from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple
import heapq


@dataclass
class Nut:
    nut_id: str
    weight: int
    expire_at: float
    location_id: str
    layer: int  # 0 是最上层


class Layer:
    def __init__(self, cap: int) -> None:
        self.cap = cap
        self.ids = set()
        self.max_heap: List[Tuple[int, str]] = []  # (-weight, nut_id)
        self.min_heap: List[Tuple[int, str]] = []  # (weight, nut_id)

    def size(self) -> int:
        return len(self.ids)

    def has_space(self) -> bool:
        return self.size() < self.cap

    def occupancy(self) -> float:
        return self.size() / self.cap if self.cap else 1.0

    def add(self, nut_id: str, weight: int) -> None:
        self.ids.add(nut_id)
        heapq.heappush(self.max_heap, (-weight, nut_id))
        heapq.heappush(self.min_heap, (weight, nut_id))

    def remove(self, nut_id: str) -> None:
        self.ids.discard(nut_id)  # 惰性删除,堆内脏数据稍后清理

    def _clean_max(self) -> None:
        while self.max_heap and self.max_heap[0][1] not in self.ids:
            heapq.heappop(self.max_heap)

    def _clean_min(self) -> None:
        while self.min_heap and self.min_heap[0][1] not in self.ids:
            heapq.heappop(self.min_heap)

    def peek_heaviest(self) -> Optional[str]:
        self._clean_max()
        return self.max_heap[0][1] if self.max_heap else None

    def pop_lightest(self) -> Optional[str]:
        self._clean_min()
        if not self.min_heap:
            return None
        _, nut_id = heapq.heappop(self.min_heap)
        self.ids.remove(nut_id)
        return nut_id


class SquirrelResearch:
    def __init__(self) -> None:
        self.locations: Dict[str, List[Layer]] = {}
        self.nuts: Dict[str, Nut] = {}
        self.expiry_heap: List[Tuple[float, str]] = []
        self.last_ts = float("-inf")

    def init(self, locations: Dict[str, int]) -> None:
        self.locations.clear()
        self.nuts.clear()
        self.expiry_heap.clear()
        self.last_ts = float("-inf")
        for loc_id, levels in locations.items():
            caps = self._fib_caps(levels)
            self.locations[loc_id] = [Layer(c) for c in caps]

    def HideNut(
        self,
        timestamp: float,
        location_id: str,
        nut_id: str,
        nut_weight: int,
        time_to_expire: float
    ) -> bool:
        if not self._tick(timestamp):
            return False
        layers = self.locations.get(location_id)
        if layers is None or nut_id in self.nuts:
            return False

        target = -1
        for i in range(len(layers) - 1, -1, -1):  # 深到浅
            if layers[i].has_space():
                target = i
                break
        if target < 0:
            return False

        exp = timestamp + time_to_expire
        self.nuts[nut_id] = Nut(nut_id, nut_weight, exp, location_id, target)
        layers[target].add(nut_id, nut_weight)
        heapq.heappush(self.expiry_heap, (exp, nut_id))
        return True

    def RetrieveNuts(
        self,
        timestamp: float,
        location_id: str,
        max_squirrel_capacity_in_nuts: int
    ) -> List[str]:
        if not self._tick(timestamp):
            return []
        layers = self.locations.get(location_id)
        if layers is None or max_squirrel_capacity_in_nuts <= 0:
            return []
        if sum(layer.size() for layer in layers) == 0:
            return []

        ans: List[str] = []
        while len(ans) < max_squirrel_capacity_in_nuts:
            reachable = self._reachable_layers(layers)
            pick = self._pick_best(layers, reachable)
            if pick is None:
                break
            nut_id, layer_idx = pick
            self._remove_nut(nut_id, with_backfill=True)
            ans.append(nut_id)
        return ans

    def _fib_caps(self, levels: int) -> List[int]:
        if levels <= 0:
            return []
        caps = [1]
        if levels == 1:
            return caps
        caps.append(2)
        for _ in range(2, levels):
            caps.append(caps[-1] + caps[-2])
        return caps

    def _tick(self, ts: float) -> bool:
        if ts <= self.last_ts:  # 全局递增
            return False
        self.last_ts = ts
        self._purge_expired(ts)
        return True

    def _purge_expired(self, ts: float) -> None:
        while self.expiry_heap and self.expiry_heap[0][0] <= ts:
            exp, nut_id = heapq.heappop(self.expiry_heap)
            nut = self.nuts.get(nut_id)
            if not nut or nut.expire_at != exp:
                continue
            self._remove_nut(nut_id, with_backfill=True)

    def _reachable_layers(self, layers: List[Layer]) -> List[int]:
        if not layers:
            return []
        r = [0]
        for i in range(len(layers) - 1):
            if layers[i].occupancy() < 0.5:
                r.append(i + 1)
            else:
                break
        return r

    def _pick_best(
        self,
        layers: List[Layer],
        reachable: List[int]
    ) -> Optional[Tuple[str, int]]:
        # 跨层比较:更重优先;同重按 nut_id 字典序
        best_id = None
        best_layer = -1
        best_weight = None
        for idx in reachable:
            nid = layers[idx].peek_heaviest()
            if nid is None:
                continue
            nut = self.nuts[nid]
            if (
                best_id is None
                or nut.weight > best_weight
                or (nut.weight == best_weight and nid < best_id)
            ):
                best_id = nid
                best_weight = nut.weight
                best_layer = idx
        return None if best_id is None else (best_id, best_layer)

    def _remove_nut(self, nut_id: str, with_backfill: bool) -> None:
        nut = self.nuts.pop(nut_id, None)
        if nut is None:
            return
        layers = self.locations[nut.location_id]
        layers[nut.layer].remove(nut_id)
        if with_backfill and nut.layer > 0:
            self._backfill(nut.location_id, nut.layer)

    def _backfill(self, location_id: str, hole_layer: int) -> None:
        # 从 hole_layer 上方逐层取最轻下落,形成连锁补位
        layers = self.locations[location_id]
        for upper in range(hole_layer - 1, -1, -1):
            moved_id = layers[upper].pop_lightest()
            if moved_id is None:
                break
            moved_nut = self.nuts[moved_id]
            layers[upper + 1].add(moved_id, moved_nut.weight)
            moved_nut.layer = upper + 1

此外,这份实现的关键复杂度很稳。
HideNut 主要是堆操作,接近 O(log N)
RetrieveNuts 每次取出也以堆操作为主。
因此,在大 N 与大 Q 下更安全。

BQ:核心考点

  • 因此,先看你能否把长规则抽成不变量。
  • 此外,看你是否能识别全部失败路径。
  • 但是,也会看你如何解释时间和过期语义。
  • 与此同时,还会看你如何压复杂度。
  • 换句话说,代码正确只是及格线。

BQ:STAR 应对策略

  1. S:因此先说场景。规则多且易错。
  2. T:随后说任务。要做高一致性模拟器。
  3. A:此外说行动。状态机+堆+哈希+惰性删除。
  4. R:最后说结果。复杂度可控,边界全覆盖。

专家备考策略与高频考点:Optiver SquirrelResearch 面试题2026

因此,备考 Optiver SquirrelResearch 面试题2026 时,先写断言表。
把非法地点、重复 ID、容量溢出先列完。
随后再写主流程。
这样能明显降低返工率。

此外,第二步是做专项压测。
你要造过期、回填、同重 ID 冲突样例。
与此同时,专测时间戳递增约束。
Optiver SquirrelResearch 面试题2026 很爱考这三类。

但是,表达顺序同样关键。
先讲模型,再讲不变量。
然后讲复杂度与可扩展点。
面试官会更快确认你的工程能力。

具体来说,可先看 权威算法参考
如果你要实战演练,可 联系我们的专家进行一对一面试辅导

总结与行动号召(CTA)

总而言之,Optiver SquirrelResearch 面试题2026 的破局点是规则一致性。
因此,你要先稳状态机。
再稳排序、过期、回填和失败分支。
现在就按本文模板手写一遍,并做计时复盘。