30 秒看懂
Jane Street 工程师 Grace 和 Nolen 使用一道已停用的单位换算题进行软件工程模拟面试。Nolen 设计并演练基于图的搜索,发现遗漏的反向换算;两人随后解释如何评估沟通、表达清晰程度和解决问题的能力。
- 1将单位换算建模为图换算事实通过转换倍率连接不同单位;查询要求找到从起始单位到目标单位的路径,或返回不可转换。
- 2编码前解释设计Nolen 介绍节点、带权边和图遍历,并向 Grace 确认她能够理解他的方案。
- 3带状态的广度优先搜索队列中的每个节点都携带当前换算结果,已访问集合则防止重复探索。
- 4遗漏的反向边演练示例发现,每条换算事实还必须提供倍率为倒数的换算,才能支持反方向。
- 5全面考察的面试复盘强调,除了问题求解,面试还重视沟通、可读代码、设计判断、细心程度和对反馈的回应。
要点建议
- 明确预期输入,以及是否可以将预处理与查询分开。
- 单位换算可以建模为带倍率的图的边,从起始单位搜索到目标单位。
- 当换算事实只提供一个方向时,反向换算需要添加倍率为倒数的边。
- 逐步演练具体例子可以暴露遗漏的查找和错误假设。
- 面试官默认更重视清晰、正确的代码和沟通,而不是巧妙的优化。
- 练习应包括编写代码、解释选择,以及在接近真实面试的时间限制下工作。
按章节阅读整理后的全文
面试准备
Grace 和 Nolen 介绍模拟面试。Grace 解释共享编辑器、语言选择和对代码可读性的重视。
Grace 和 Nolen 都是 Jane Street 软件工程师。他们制作这场模拟面试,让面试过程不再那么神秘,并在之后复盘面试官关注的内容以及候选人如何准备。Grace 扮演面试官,Nolen 扮演候选人;部分建议专门针对 Jane Street。
Grace 解释说,两人将在共享编辑器中工作。Nolen 可以使用自己最熟悉的语言,面试期间不会运行代码。她要求编写真实代码,但不要求标点完全无误,也不要求记住每个标准库 API;双方可以商定一个合理的接口。
Grace 说,比起一开始就写出最优化的方案,她更重视清晰、正确、可读的代码。如有需要,她之后可以要求更快的版本。她还会在最后留出时间让候选人提问。
单位换算问题
Nolen 澄清接口,并将换算事实建模为图。他先选择直接的遍历方法,再考虑优化。
题目要求根据一组换算事实回答单位换算查询。示例包括一米等于 3.28 英尺、一英尺等于 12 英寸,以及小时到分钟、分钟到秒的换算。换算事实以字符串—浮点数—字符串元组提供,查询以数值—起始单位—目标单位元组提供;不需要解析文本。

Nolen 询问是否应该将预处理与查询分开。Grace 让他自行选择,他倾向于先构建一次数据表示,供多个查询使用。他认识到单位形成互不连通的组:长度换算构成一个图,时间换算构成另一个图。
Nolen 解释图模型:单位是节点,换算事实是标注了倍率的边。回答查询时,从起始节点出发向目标遍历,沿路径将数值乘以各个换算因子。如果无法到达目标,就返回单位不可转换。
他最初的表示是一个从单位名称映射到图节点的字典,边携带倍率和相邻节点。他记得并查集可能提供更巧妙的替代方案,但想不起细节,于是询问是否可以继续使用直接的方法。Grace 同意了。
实现搜索
Nolen 构建节点和边结构,然后实现携带换算结果与已访问集合的广度优先搜索。Grace 提问并指出小错误。
Nolen 草拟 Edge 和 Node 类,明确数据表示。接着他开始编写预处理函数,遍历换算事实,为尚不存在的单位名称创建节点,并连接相应节点。他说,先把结构写出来之后,或许可以再简化 Python 代码。

开始处理查询时,Nolen 考虑先收集所有倍率,最后再合并,但随后改变主意:携带当前数值并在经过每条边时相乘可能更清楚。Grace 询问变化的原因,他解释了自己的思路,而不是默默继续。
Nolen 提议使用队列,保存单位及以该单位表示的数值。对每条出边,将相邻单位连同数值乘以该边换算因子后的结果加入队列。已访问集合可以防止重复探索相同节点。
他选择广度优先搜索,因为它能找到边数较少的路径;他希望这样可以减少多个因子相乘所积累的浮点舍入误差。Grace 确认,这种方法是在广度优先搜索的每个队列项中携带额外状态。
编写代码时,Nolen 回到 Node 类并添加边列表。Grace 指出草拟的 Edge 类尚未使用,Nolen 随即修正。他用起始节点和数值初始化队列,让主循环能够一致地处理初始状态。
Nolen 不记得 Python 队列 API 的确切形式,因此 Grace 允许他假定一个合理的接口。在循环中,他取出节点和数值:若该节点是目标,就返回数值;否则探索其边。Grace 发现他把元组顺序写反了,他进行了修正。

遍历时,在将目标节点加入队列之前,先把当前数值乘以该边的换算因子。Nolen 注意到,如果只在节点出队后标记,队列中可能多次出现同一节点,于是调整将节点加入已访问集合的时机。如果队列耗尽,他返回 None。
演练并修正方案
一个具体例子暴露了遗漏的节点查找,以及添加倒数边的必要性。两人没有执行代码便结束了模拟面试。
Grace 请 Nolen 逐步演练一个例子。他首先注意到查询函数没有在预处理后的换算事实中查找起始和目标单位。他添加了对未知单位的检查,取出对应节点,然后才开始追踪算法。
将两米换算成英寸时,队列从米和数值 2 开始。沿米到英尺的边乘以 3.28,得到 6.56 英尺;再沿英尺到英寸的边乘以 12,得到 78.72 英寸。演练同时检查了状态和控制流程。
演练暴露了另一个问题:当前的图只记录每条事实提供的方向。它可以将米转换为英寸,却可能无法反向转换。Nolen 添加了一条倍率为倒数的反向边,让英尺到英寸的事实也能提供英寸到英尺的换算。
Nolen 认为,加入反向边后,同一遍历方法也能回答反向查询。Grace 结束模拟面试;由于 Nolen 已经在 Jane Street 工作,两人跳过通常留给候选人的提问时间,转入复盘。共享编辑器中的代码经过讨论,但没有执行。
面试官关注什么
两位工程师讨论沟通、清晰正确的代码,以及如何在真实的面试情境中练习。
在复盘中,Grace 和 Nolen 说,主要目标是了解与候选人共事会是什么样。解决问题很重要,但并非唯一标准:他们还关注沟通、代码清晰程度、设计直觉、细心程度、对反馈的回应、相关专业能力和一般问题求解能力。
他们提醒,不要把“边思考边说”理解成必须叙述每一行代码。沟通能帮助面试官和候选人保持同步,让面试官理解方案并在需要时提供帮助。澄清题目要求,或在实现前讲清方法,可能比逐项解说更有用。
边思考边说本身可能很难,候选人可以表示自己想先安静思考片刻。两位讲者也强调清晰、正确的代码:清楚的控制流、有用的辅助函数和好的命名,能让其他人更容易在大型共享代码库中工作。
除非优化本身是题目的一部分,直接的方案通常比巧妙的方案更好。他们最重要的建议是练习:设置计时器,自己实现方案,并像面对面试官一样解释思路,而不是仅读完答案就假设自己也能写出来。
同频道的其他文章
1:48:29EN → 简体中文
1:05:45EN → 简体中文
1:25:00EN → 简体中文
1:16:29EN → 简体中文
4:41EN → 简体中文
1:34:29EN → 简体中文