← 返回面經列表

Google VO 真題:刪除葉子節點得到合法序列,遞迴為什麼是最自然的第一反應?

6 分鐘

Google VO Tree Problem

Google VO 裡經常會出現一類題:題面看起來不複雜,但非常考察你能不能先把依賴關係抽象清楚,再決定用遞迴、圖模型還是模擬過程去做。

這道題就是很典型的例子。

題意是:

給定一棵樹。每次可以選一個葉子節點並刪除。要求你輸出在最後刪完整棵樹的過程中,任意一個合法的刪除序列。

換句話說,只要你的序列滿足:

  • 每次刪掉的節點,在當時確實是葉子
  • 最後所有節點都被刪完

那這個序列就是合法答案。


這題一開始最重要的是先 clarify

這題本身不難,但非常適合在開場用 clarify 把邊界定清楚。因為不同設定會直接影響你後面的講法。

這道題比較值得先確認的點有四個:

1. 是二叉樹還是多叉樹?

面試裡比較合理的處理方式通常是:

  • 先問
  • 如果對方沒有特別指定,先按二叉樹講清楚
  • 再主動補一句:多叉樹思路本質一致

這樣既沒有擅自假設,也不會一開始就把問題講太複雜。

2. 需不需要考慮輸入不是合法樹的情況?

如果面試官說不用,那整個問題就可以直接建立在「輸入保證合法」之上,不需要額外做環檢查或合法性驗證。

這種 clarify 很重要,因為它決定了你是否要把時間花在不必要的 defensive programming 上。

3. 輸入規模是否適合遞迴?

如果面試官明確說可以用遞迴,那就非常關鍵。因為這題在「只要求輸出任意一個合法序列」的版本下,遞迴幾乎就是最自然的第一解。

4. 是一次性生成完整答案,還是每次呼叫回傳一個刪除節點?

這點會直接決定你是:

  • 做一個後序遍歷式的整體輸出
  • 還是設計一個可逐步產出節點的狀態機 / iterator

如果是一次性生成,那遞迴解法會非常順。


為什麼這題一旦只要求「任意一個合法序列」,就幾乎等於遞迴題

這題最核心的約束其實只有一句話:

父節點必須在所有子節點刪完之後,才能被刪除。

一旦你把它翻譯成這個形式,問題就立刻變得非常像樹上的後序遍歷。

因為後序遍歷本來就是:

  • 先處理子節點
  • 再處理父節點

而在這道題裡,「處理」剛好就可以理解為「刪除並加入答案序列」。

所以從建模角度看,這題最自然的想法就是:

  • 先遞迴刪掉每棵子樹
  • 等所有子節點都刪完,當前節點自然就會變成葉子
  • 這時再刪掉當前節點

這也就是為什麼它會被很多人第一眼認成遞迴題。


遞迴解法應該怎麼講

如果你在面試裡講這題,最穩的方式不是一上來就寫程式,而是先把遞迴函數的職責定義清楚。

比較自然的定義是:

遞迴函數負責返回「刪除當前子樹所對應的一個合法序列」。

圍繞這個定義,整個過程其實很順:

i. 如果當前節點是 None

那就直接 return。

因為空樹不需要刪除任何東西,也不會對答案產生影響。

ii. 遍歷當前節點的所有子節點

對每個子節點遞迴地生成刪除序列。

這一步的意義是先保證下面的子樹全部被清空。

iii. 當所有子節點都處理完

從邏輯上說,這些孩子都已經被刪掉了。也就是說,當前節點此時已經沒有孩子,因此自然變成了葉子。

iv. 將當前節點加入答案序列

因為它現在已經滿足「葉子節點可刪」的條件,所以把它加入序列就是合法的。

整套邏輯和樹的後序遍歷幾乎完全一致。


為什麼二叉樹和多叉樹本質上沒差

這題非常適合在講完二叉樹版本後,主動補一句:

如果擴展到多叉樹,思路完全一樣,只是從「遞迴左右子樹」變成「遞迴遍歷所有孩子」。

這句話很加分,因為它說明你沒有把方法綁死在二叉樹結構上,而是看到了它真正依賴的只是:

  • 節點到孩子的層級關係
  • 「孩子先於父親刪除」的偏序約束

所以從二叉樹推廣到多叉樹時,核心模型完全不變。


這題為什麼是合法的

面試裡最好不要只說「像後序遍歷」,還要補一句為什麼正確。

證明其實很直觀:

  • 任意一個節點只有在所有孩子都已經刪完後,才會被加入答案
  • 所以它被加入時,一定已經是葉子
  • 因此序列中的每一步刪除都合法

最終所有節點都會恰好被訪問一次並加入答案,所以整棵樹也一定會被刪空。

這就是 correctness 的核心。


Follow-up:如果不是一次性生成,而是要一個個回傳怎麼辦?

這是這道題最自然也最有價值的 follow-up。

因為它把問題從「靜態生成一個答案」推進到了「動態地按順序產出節點」。

這時常見有兩個方向可以講。


方向一:用圖模型做「從葉子開始的拓撲排序」

這是一個非常漂亮的 follow-up 視角。

如果你把每條樹邊的方向反過來看,把「孩子刪完父親才能刪」理解成一種依賴關係,那麼:

  • 當前葉子節點可以立刻輸出
  • 當一個葉子被刪掉,它對父節點的一條依賴也被解除
  • 如果父節點的所有孩子都刪完了,它就會變成新的可刪節點

這和拓撲排序本質上一樣。

更具體地說

可以把每個節點還剩多少個「未刪除孩子」看成它的入度或剩餘依賴數。

初始化時:

  • 所有葉子節點都可以先入隊

然後每次:

  1. 從隊列裡取一個當前可刪的葉子
  2. 輸出它
  3. 找到它的父節點,把父節點的剩餘孩子數減一
  4. 如果父節點的剩餘孩子數變成 0,就把父節點入隊

這樣最終的出隊順序,就是一個合法刪除序列。

這個方法為什麼好

因為它非常適合「一個個回傳答案」的場景。

你不一定要一次性構造完整序列,而是可以每次從隊列裡彈出一個當前合法刪除的節點。

這就很像把原本的離線遞迴方案,變成了一個線上生成器。


方向二:用棧模擬遞迴過程

另一種 follow-up 的說法是:

如果我們不想真的用遞迴,也可以自己維護一個棧,去模擬後序遍歷的展開過程。

這條路的本質不是換算法,而是換執行方式。

原本遞迴棧會幫你記住:

  • 當前處理到哪個節點
  • 哪些孩子已經處理完
  • 什麼時候可以回到父節點

如果不用系統遞迴,就自己把這些狀態存起來。

這種方法的好處是:

  • 避免依賴語言呼叫棧
  • 更適合某些場景下做迭代實作

但如果題目沒有明確限制不能遞迴,這一版通常更適合作為 follow-up,而不是第一解。


這題真正考察的能力是什麼

這題表面像樹題,但實際上在考三個層次:

第一層:你能不能快速抓住偏序關係

也就是:

  • 子節點必須先刪
  • 父節點必須後刪

一旦看清這個順序關係,後面的解法都會自然很多。

第二層:你能不能從「遞迴解」切換到「線上生成解」

第一解用遞迴通常不難。
真正拉開差距的是:當面試官把題目改成「一個個生成節點」時,你能不能迅速意識到這已經更像拓撲排序或狀態模擬。

第三層:你能不能把樹和圖的視角打通

這也是 Google 很喜歡看的地方。

同一題,既可以從樹的後序遍歷角度解釋,也可以從「刪除依賴」的圖模型角度解釋。如果你能主動切換視角,面試官通常會覺得你理解得更深。


這題在 Google VO 裡為什麼很舒服

因為它不是靠技巧卡人,而是很適合看候選人是不是有清楚的建模能力。

你如果只會背模板,會覺得它像普通樹題。
你如果真的理解順序約束,就會發現:

  • 第一問是後序遍歷
  • follow-up 是葉子驅動的拓撲排序
  • 再往下還能聊遞迴與迭代模擬的實作差異

這就是很典型的 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]