五分鐘看懂一道中等難度的算法題

今天分享的題目來源於 LeetCode 第 450 號問題:刪除二叉搜索樹中的節點。雖然它的難度是 中等,但實際上很好理解,請往下看!

題目描述

給定一個二叉搜索樹的根節點 root 和一個值 key,刪除二叉搜索樹中的 key 對應的節點,並保證二叉搜索樹的性質不變。返回二叉搜索樹(有可能被更新)的根節點的引用。

一般來說,刪除節點可分爲兩個步驟:

  • 首先找到需要刪除的節點;

  • 如果找到了,刪除它。

說明:要求算法時間複雜度爲 O(h),h 爲樹的高度。

示例:

 

root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7

給定需要刪除的節點值是 3,所以我們首先找到 3 這個節點,然後刪除它。

一個正確的答案是 [5,4,6,2,null,null,7], 如下圖所示。


    5
   / \
  4   6
 /     \
2       7

另一個正確答案是 [5,2,6,null,4,null,7]。


    5
   / \
  2   6
   \   \
    4   7

題目解析

在二叉搜索樹上刪除一個節點,這道題目有一個隱含的條件,就是樹上節點的值不重複。

另外題目還要求時間複雜度需要保證 O(h) 這裏的 h 表示的是二叉樹的高度。

其實這個題目是分成兩個步驟的,第一個是找到對應的節點,第二個是刪除節點

因爲是二叉搜索樹,對於樹上每個節點來說,其 右子樹的節點都要大於其左子樹的節點,那麼要找對應節點,我們可以從根節點開始,一路比較,大的話就去右邊找,小的話就去左邊找,這樣每次我們都往下,可以保證時間複雜度是 O(h)

當我們找到了要刪除的節點,在刪除這一步就會有很多的細節,主要是因爲我們需要調整餘下的結構,以維持二叉搜索樹的性質。

針對這個問題,我們可以分情況討論:

 

        5
       / \
      3   6
     / \   \
    2   4   7
   /         \
  1           8
  • 情況 1:當刪除的節點沒有左右子樹,比如上圖的 4、8、1
    這時直接刪除即可,樹依舊可以保持二叉搜索樹的性質

  • 情況 2:當刪除的節點有左子樹沒有右子樹,比如上圖的 2
    這時我們只需要將整個左子樹移到當前位置即可
    也就是將左子樹的根節點放到刪除節點的位置,其餘不變

  • 情況 3:當刪除的節點沒有左子樹有右子樹,比如上圖的 6、7
    這時我們只需要將整個右子樹移到當前位置即可
    也就是將右子樹的根節點放到刪除節點的位置,其餘不變

  • 情況 4:當刪除的節點既有左子樹又有右子樹,比如上圖的 5、3
    這時就有兩種方法供選擇:
    去到左子樹中,找到值最大節點,將右子樹全部移到這個節點下
    去到右子樹中,找到值最小節點,將左子樹全部移到這個節點下

通過上面的討論分析,我們有了大致的思路。在實現方面,我們可以藉助遞歸來巧妙地達到刪除對應節點的目的。

圖片描述

image

image

image

image

image

image

image

image

image

image

參考代碼

 

//五分鐘學算法
public TreeNode deleteNode(TreeNode root, int key) {
    if (root == null) {
        return null;
    }

    // 當前遍歷到的節點大於要找的節點,去左邊繼續找
    if (root.val > key) {
        root.left = deleteNode(root.left, key);
    }
    // 當前遍歷到的節點小於要找的節點,去右邊繼續找
    else if (root.val < key) {
        root.right = deleteNode(root.right, key);
    } 
    // 找到要刪除的節點,進行刪除操作
    else {
        // 情況 1 & 2
        if (root.right == null) {
            return root.left;
        } 

        // 情況 3
        if (root.left == null) {
            return root.right;
        }

        // 去到刪除節點的右子樹,找到值最小的節點
        TreeNode rightSmallest = root.right;
        while (rightSmallest.left != null) {
            rightSmallest = rightSmallest.left;
        }

        // 將刪除節點的左子樹全部移到這個節點下
        rightSmallest.left = root.left;

        // 返回右子樹的根節點,放到當前刪除節點的位置
        return root.right;
    }

    return root;
}



作者:碼農小光
鏈接:https://www.jianshu.com/p/89a199fc75e5
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章