
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 視角。
如果你把每條樹邊的方向反過來看,把「孩子刪完父親才能刪」理解成一種依賴關係,那麼:
- 當前葉子節點可以立刻輸出
- 當一個葉子被刪掉,它對父節點的一條依賴也被解除
- 如果父節點的所有孩子都刪完了,它就會變成新的可刪節點
這和拓撲排序本質上一樣。
更具體地說
可以把每個節點還剩多少個「未刪除孩子」看成它的入度或剩餘依賴數。
初始化時:
- 所有葉子節點都可以先入隊
然後每次:
- 從隊列裡取一個當前可刪的葉子
- 輸出它
- 找到它的父節點,把父節點的剩餘孩子數減一
- 如果父節點的剩餘孩子數變成 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]