1. 程式人生 > 實用技巧 >LeetCode Notes_#450_刪除二叉搜尋樹中的節點

LeetCode Notes_#450_刪除二叉搜尋樹中的節點

LeetCode Notes_#450_刪除二叉搜尋樹中的節點

Contents

題目

給定一個二叉搜尋樹的根節點 root 和一個值 key,刪除二叉搜尋樹中的key對應的節點,並保證二叉搜尋樹的性質不變。返回二叉搜尋樹(有可能被更新)的根節點的引用。
一般來說,刪除節點可分為兩個步驟:

  1. 首先找到需要刪除的節點;
  2. 如果找到了,刪除它。
    說明: 要求演算法時間複雜度為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

思路分析

BST的刪除操作是BST資料結構最難的一個操作,更詳細的講解可以參考 《演算法第四版》3.2.3.5小節
主要分為兩步:
1. 找到待刪除節點
不能把搜尋和刪除兩步操作分離,因為刪除節點後,還要把新的子樹連線到父節點。這個操作只能通過遞迴的回溯來進行。

       if(key < root.val){
            //因為key節點在左子樹當中,所以就縮小範圍,在左子樹當中刪除key節點
            root.left = deleteNode(root.left, key);
            //遞迴呼叫結束,把刪除後的子樹連線到root上面
return root; } else if(key > root.val){ //同理,右子樹的情況 root.right = deleteNode(root.right, key); return root; }

2. 刪除待刪除節點
刪除操作需要分情況考慮,有如下的三種情況:
1. 待刪除節點沒有子節點,可以直接將其刪除

2. 待刪除節點只有一個子節點,用它的子節點代替它

3. 待刪除節點有兩個子節點,則需要用待刪除節點在中序遍歷中的前驅或者後繼代替它

  • 前驅節點一定是左子樹的最大節點(也就是左子樹最右下角的節點)
  • 後繼節點一定是右子樹的最小節點(也就是右子樹最左下角的節點)

總結來說,BST在刪除節點之前,中序遍歷有序。刪除節點之後,中序遍歷應該還是有序的。

解答

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null) return null;
        if(key < root.val){
            //因為key節點在左子樹當中,所以就縮小範圍,在左子樹當中刪除key節點
            root.left = deleteNode(root.left, key);
            //遞迴呼叫結束,把刪除後的子樹連線到root上面
            return root;
        }
        else if(key > root.val){
            //同理,右子樹的情況
            root.right = deleteNode(root.right, key);
            return root;
        }
        //key == root.val,即遇到的root剛好就是待刪除的節點
        else{
            //左子樹為null,用右子樹替代key節點(左右子樹同時為null,也正確)
            if(root.left == null) return root.right;
            ////右子樹為null,用左子樹替代key節點
            else if(root.right == null) return root.left;
            else{
                //後繼節點其實就是右子樹裡的最小值
                TreeNode successor = min(root.right);
                //在右子樹當中需要刪除successor,因為successor要和key交換位置
                successor.right = deleteMin(root.right);
                //左子樹沒有變化
                successor.left = root.left;
                return successor;
            }

        }
    }

    //找到BST當中的最小節點,返回這個節點
    private TreeNode min(TreeNode node){
        if(node.left == null) return node;
        return min(node.left);
    }

    //刪除BST當中的最小節點,返回刪除之後的樹的根節點
    private TreeNode deleteMin(TreeNode node){
        //找到最小節點,沒有左孩子,用它的右子樹代替它
        if(node.left == null) return node.right;
        node.left = deleteMin(node.left);
        return node;
    }
}

複雜度分析

時間複雜度:O(h)
空間複雜度:O(1)