寶寶也能看懂的 leetcode 周賽 - 172 - 3

1325. 刪除給定值的葉子節點

Hi 你們好,我是張小豬。歡迎來到『寶寶也能看懂』系列之 leetcode 周賽題解。node

這裏是第 172 期的第 3 題,也是題目列表中的第 1325 題 -- 『刪除給定值的葉子節點』git

題目描述

給你一棵以 root 爲根的二叉樹和一個整數 target,請你刪除全部值爲 target 的 葉子節點github

注意,一旦刪除值爲 target 的葉子節點,它的父節點就可能變成葉子節點;若是新葉子節點的值剛好也是 target,那麼這個節點也應該被刪除。shell

也就是說,你須要重複此過程直到不能繼續刪除。segmentfault

示例 1:優化

1325-1.png

輸入:root = [1,2,3,2,null,2,4], target = 2
輸出:[1,null,3,null,4]
解釋:
上面左邊的圖中,綠色節點爲葉子節點,且它們的值與 target 相同(同爲 2 ),它們會被刪除,獲得中間的圖。
有一個新的節點變成了葉子節點且它的值與 target 相同,因此將再次進行刪除,從而獲得最右邊的圖。

示例 2:spa

1325-2.png

輸入:root = [1,3,3,3,2], target = 3
輸出:[1,3,null,null,2]

示例 3:3d

1325-3.png

輸入:root = [1,2,null,2,null,2], target = 2
輸出:[1]
解釋:每一步都刪除一個綠色的葉子節點(值爲 2)。

示例 4:指針

輸入:root = [1,1,1], target = 1
輸出:[]

示例 5:code

輸入:root = [1,2,3], target = 1
輸出:[1,2,3]

提示:

  • 1 <= target <= 1000
  • 每一棵樹最多有 3000 個節點。
  • 每個節點值的範圍是 [1, 1000]

官方難度

MEDIUM

解決思路

題目給定了一個二叉樹和一個目標值 target,咱們須要刪除全部值爲 target 的葉節點。這裏須要注意的是,若是某一個節點自己不是葉節點,可是它的葉節點由於值的緣由被刪除了,那麼這時候它便成爲了新的葉節點。因而也須要對它的值進行判斷和處理。

讀完題目後,小豬第一反應是,這又是一道很是套路的題。相信熟悉套路的小夥伴們已經有思路了吧,不過咱們這裏仍是一步一步來。

首先,第一個須要解決的問題是,咱們須要判斷出符合要求的葉節點。葉節點即爲沒有任何子節點的節點。因爲咱們這裏是二叉樹,而且題目給定的結構中用特定的 null 值來標識空節點,因此咱們只須要判斷該節點的左右子節點是否爲 null 值,而後判斷該節點的值是否爲目標值 target 便可。例如:

node.left === null && node.right === null && node.val === target

這裏能夠作一個小小的優化,減小一個判斷。由於各個節點的對象都是不同的,哪怕值同樣那個節點也是不一樣的節點。這時候只有當兩個節點都是空值 null 的時候纔會值相等,因此咱們能夠直接判斷左右兩個子節點是否相等,便可完成左右兩個子節點是否都是空值的判斷。例如:

node.left === node.right && node.val === target

有告終果的判斷邏輯以後,接下來就是如何遍歷這個二叉樹。根據題目要求,因爲子節點的處理結果會影響到父節點的處理過程,因此咱們應當先訪問左右兩個子節點,再訪問節點自己。這也就對應着二叉樹遍歷方式中的後序遍歷。即節點遍歷順序爲左子節點 -> 右子節點 -> 節點自己。

有了遍歷過程,接下來的就是返回值的處理。由於若是當前節點須要被刪除,那麼方式實際上是經過修改父節點對應的指針。而父節點咱們沒法直接在當前節點訪問到。若是從新從根節點開始查找當前節點的父節點,咱們還須要額外的遍歷開銷。因此這裏考慮經過遞歸調用的返回值來作處理。

直接方案

基於上面的思路,咱們能夠獲得以下的遞歸流程:

  1. 若是左子節點存在,遞歸的處理當前節點的左子節點,並更新左子節點的值。
  2. 若是右子節點存在,遞歸的處理當前節點的右子節點,並更新右子節點的值。
  3. 判斷當前節點是不是符合要求的葉節點,並返回當前節點的最新值。

可能有的小夥伴會問,這個遞歸怎麼沒有終止條件呢?其實這裏終止條件隱藏在處理邏輯裏了。仔細觀察咱們的邏輯就會發現,判斷左子節點存在和判斷右子節點存在,其實就是遞歸進行下去的終止條件。

基於以上流程,咱們能夠實現相似下面的代碼:

const removeLeafNodes = (node, target) => {
  node.left && (node.left = removeLeafNodes(node.left, target));
  node.right && (node.right = removeLeafNodes(node.right, target));
  return node.left === node.right && node.val === target ? null : node;
};

基於遞歸來實現看起來的確很簡潔。這裏咱們也能夠寫成一行代碼來實現,不過這一行會比較長一點。例以下面這樣:

const removeLeafNodes = (node, target) => (node.left && (node.left = removeLeafNodes(node.left, target)), node.right && (node.right = removeLeafNodes(node.right, target)), node.left === node.right && node.val === target ? null : node);

總結

這是一道很是套路的題,用來考察二叉樹的遍歷過程。題目並不難,實現的代碼也不多。因此上面的內容着重在於前期的思路分析。但願熟悉套路的小夥伴們不要鄙視小豬廢話太多啦,也但願藉此讓不熟悉套路的小夥伴們摸到這個套路的方法。

加油武漢,天佑中華

相關連接

qrcode_green.jpeg

相關文章
相關標籤/搜索