← 返回博客列表
Google

Google VO 真题:删除叶子节点得到合法序列,递归为什么是最自然的第一反应?

2026-04-07

Google VO Tree Problem

Google VO 里经常会出现一类题:题面看起来不复杂,但非常考察你能不能先把问题抽象清楚,再决定用递归、图模型还是模拟过程去做。

这道题就是一个很典型的例子。

题意是:

给定一棵树。每次可以选择一个叶子节点并删除。要求你输出在最终删完整棵树的过程中,任意一个合法的删除序列。

换句话说,只要你的序列满足:

那这个序列就是合法答案。


这题一开始最重要的是先 clarify

这题本身不难,但非常适合在开场用 clarify 把边界定清楚。因为不同设定会直接影响解法表述。

这道题比较值得先确认的点有四个:

1. 是二叉树还是多叉树?

面试里最合理的处理方式通常是:

这样做的好处是,你既没有擅自假设,也没有把问题讲复杂。

2. 需不需要考虑输入不是合法树的情况?

如果面试官说不用,那整个问题就可以直接建立在“输入保证合法”之上,不需要额外再做环检测或连通性判断。

这种 clarify 很重要,因为它决定了你是否要把时间花在无关的 defensive programming 上。

3. 输入规模是否适合递归?

如果面试官明确说可以用递归,那就非常关键。因为这题在“只要求输出任意一个合法序列”的版本下,递归几乎就是最自然的第一解。

4. 是一次性生成完整答案,还是每次调用返回一个删除节点?

这点会直接决定你是:

如果是一次性生成,那递归解法会非常顺。


为什么这题一旦只要求“任意一个合法序列”,就几乎等于递归题

这题最核心的约束其实只有一句话:

父节点必须在所有子节点删完之后,才能被删除。

一旦你把它翻译成这个形式,问题就立刻变得非常像树上的后序遍历。

因为后序遍历本来就是:

而在这道题里,“处理”刚好就可以理解为“删除并加入答案序列”。

所以从建模角度看,这题最自然的想法就是:

这也就是为什么它会被很多人第一眼认成递归题。


递归解法应该怎么讲

如果你在面试里讲这题,最稳的方式不是上来就写代码,而是先把递归函数的职责定义清楚。

比较自然的定义是:

递归函数负责返回“删除当前子树所对应的一个合法序列”。

围绕这个定义,整个过程其实非常顺:

i. 如果当前节点是 None

那就直接返回。

因为空树不需要删除任何东西,也不会对答案产生影响。

ii. 遍历当前节点的所有子节点

对每个子节点递归地生成删除序列。

这个步骤的意义是先保证下面的子树全部被清空。

iii. 当所有子节点都处理完

从逻辑上说,这些孩子都已经被删掉了。也就是说,当前节点此时已经没有孩子了,于是它自然变成了叶子。

iv. 将当前节点加入答案序列

因为它现在已经满足“叶子节点可删”的条件,所以当前节点被加入序列就是合法的。

整套逻辑和树的后序遍历几乎完全一致。


为什么二叉树和多叉树本质上没区别

这题非常适合在讲完二叉树版本后主动补一句:

如果扩展到多叉树,思路完全一样,只是从“递归左右子树”变成“递归遍历所有孩子”。

这句话很加分,因为它说明你没有把方法绑死在二叉树结构上,而是看到了它真正依赖的只是:

所以从二叉树推广到多叉树时,核心模型完全不变。


这题为什么是合法的

面试里最好不要只说“像后序遍历”,还要补一句为什么正确。

证明其实很直观:

最终所有节点都会恰好被访问一次并加入答案,所以整个树也一定会被删空。

这就是 correctness 的核心。


Follow-up:如果不是一次性生成,而是要一个个返回怎么办?

这是这道题最自然也最有价值的 follow-up。

因为它把问题从“静态生成一个答案”推进到了“动态地按顺序产出节点”。

这时常见有两个方向可以讲。


方向一:用图模型做“从叶子开始的拓扑排序”

这是一个非常漂亮的 follow-up 视角。

如果你把每条树边的方向反过来看,把“孩子删完父亲才能删”理解成一种依赖关系,那么:

这和拓扑排序本质上一样。

更具体地说

可以把每个节点还剩多少个“未删除孩子”看成它的入度或剩余依赖数。

初始化时:

然后每次:

  1. 从队列里取一个当前可删的叶子
  2. 输出它
  3. 找到它的父节点,把父节点的剩余孩子数减一
  4. 如果父节点的剩余孩子数变成 0,就把父节点入队

这样最终的出队顺序,就是一个合法删除序列。

这个方法为什么好

因为它特别适合“一个个返回答案”的场景。

你不一定需要一次性构造完整序列,而是可以每次从队列里弹出一个当前合法删除的节点。

这就很像把原来的离线递归方案,变成了一个在线生成器。


方向二:用栈模拟递归过程

另一种 follow-up 说法是:

如果我们不想真的用递归,也可以手动维护一个栈,去模拟后序遍历的展开过程。

这条路的本质不是换算法,而是换执行方式。

原本递归栈帮你记录:

如果不用系统递归,就自己维护这些状态。

这种方法的好处是:

但从表达上来说,如果题目没有明确限制递归,这一版通常更适合作为 follow-up,而不是第一解。


这题真正考察的能力是什么

这题表面像树题,但实际上在考三个层次:

第一层:你能不能快速抓住偏序关系

也就是:

一旦看清这个顺序关系,后面的方法都会自然很多。

第二层:你能不能从“递归解”切换到“在线生成解”

第一解用递归通常不难。
真正拉开差距的是:当面试官把题改成“一个个生成节点”时,你能不能迅速意识到这已经更像拓扑排序或状态模拟问题。

第三层:你能不能把树和图的视角打通

这也是 Google 很喜欢看的地方。

同一题,既可以从树的后序遍历角度解释,也可以从“删除依赖”的图模型角度解释。如果你能主动切换视角,面试官通常会觉得你理解得更深。


这题在 Google VO 里为什么很舒服

因为它不是靠技巧卡人,而是很适合看候选人是不是有清晰的建模能力。

你如果只会背模板,会觉得它像普通树题。
你如果真的理解顺序约束,就会发现:

这就是很典型的 Google VO 风格:

原题不一定难,但 follow-up 会看你能不能把一个模型讲透。


📌 最后总结

这道题最值得记住的核心,不是“删除叶子节点”这句话本身,而是它背后的偏序关系:

只要看到这一点:

如果你最近在准备 Google VO,这类题很值得反复练。因为它特别能体现你是不是只会写代码,还是已经能把递归、图和顺序约束统一到一个模型里讲清楚。


🚀 oavoservice:你的 Google VO 稳定输出保障

Google VO 很多题真正难的都不是第一问,而是后续 follow-up 一层层往下压时,你能不能继续把模型讲清楚。

我们提供:

Google VO 实时辅助 — Coding、BQ、System Design 全程支持
原题 mock — 尽可能还原真实 Google 面试节奏
高质量 follow-up 训练 — 不只练会做,更练会讲
真实面经沉淀 — 持续整理最新 Google 高频轮次

如果你也想拿真实题感做 mock,或者想在正式面试前把表达稳定住,欢迎直接来聊。

👉 立即添加微信:Coding0201

Telegram: @OAVOProxy
Gmail: [email protected]