题目重点:Staff VO 题目要求把员工到经理二维数组输出为最高级经理→中层→员工;本文处理多根、重复边、双经理与环。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版每对 (employee,manager) 表示员工只有这一名直接经理;同一对重复输入幂等,不同经理冲突报错。
- 只有在边中出现的人会进入森林;孤立员工若没有任何边,输入并未提供名单,无法凭空输出。多根允许,根与同层下属按名称字典序排列。
- 环或自管理是非法组织图,报错;输出嵌套对象 name/reports,不规定为题目格式。
解题路线
- 单次扫描建立 employee→manager 和 manager→children,同时记录所有人;发现员工映射到两个不同经理立即拒绝。
- 根节点是出现在 people 但不在 employee 键集合里的人,可能有多个;对每根 DFS 按稳定顺序构造嵌套 reports。
- DFS 记录已访问人;若遇再次访问或结束后已访问数少于总人数,图有环或非法共享节点。
- 随机生成无环森林并打乱边顺序,独立检查每个非根节点的 parent、根集合、稳定顺序和无重复;另外手测纯环及双经理。
手动走一遍
边 (A,M)、(B,M)、(M,CEO)、(C,N) 得两个根 CEO 与 N;输出 CEO→M→{A,B} 以及 N→C。若再加 (A,N),A 有两名直接经理,训练版拒绝而非悄悄覆盖。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
"""Build a stable top-down reporting forest from (employee, manager) pairs."""
def reporting_forest(pairs: list[tuple[str, str]]) -> list[dict]:
parent = {}
children = {}
people = set()
for employee, manager in pairs:
if employee == manager:
raise ValueError("self manager")
if employee in parent and parent[employee] != manager:
raise ValueError("multiple managers")
parent[employee] = manager
people.update((employee, manager))
children.setdefault(manager, set()).add(employee)
roots = sorted(people - parent.keys())
seen = set()
def build(person: str) -> dict:
if person in seen:
raise ValueError("cycle")
seen.add(person)
return {"name": person, "reports": [build(child) for child in sorted(children.get(person, ()))]}
forest = [build(root) for root in roots]
if seen != people:
raise ValueError("cycle")
return forest
复杂度
N 人、E 输入边,建索引期望 O(E),根及孩子排序总最坏 O(N log N),DFS O(N+E),空间 O(N+E)。若输入承诺唯一父节点且不要求排序,可达到 O(N+E)。
易错点与追问
容易失分的边界
- 把 employee→manager 方向当作根到叶直接遍历,找不到下属。
- 只找一个 CEO,漏掉多棵组织树。
- 员工双经理时后写覆盖前写,结果受输入顺序影响。
- 纯环无根时直接返回空森林,掩盖非法输入。
- 从二维数组假定存在未列出的孤立员工。
继续深挖
- 若是矩阵式汇报,允许多经理,输出应改成 DAG 还是重复展示节点?
- 若有人离职/转组,怎样增量维护森林与祖先查询?
- 若组织名单含孤立人员但边表不含,接口如何补充?
