Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST.
Basically, the deletion can be divided into two stages:
- Search for a node to remove.
- If the node is found, delete the node.
Note: Time complexity should be O(height of tree).
Example:
root = [5,3,6,2,4,null,7] key = 3 5 / \ 3 6 / \ \ 2 4 7 Given key to delete is 3. So we find the node with value 3 and delete it. One valid answer is [5,4,6,2,null,null,7], shown in the following BST. 5 / \ 4 6 / \ 2 7 Another valid answer is [5,2,6,null,4,null,7]. 5 / \ 2 6 \ \ 4 7
這道題讓我們刪除二叉搜索樹中的一個節點,難點在於刪除完結點並補上那個結點的位置后還應該是一棵二叉搜索樹。被刪除掉的結點位置,不一定是由其的左右子結點補上,比如下面這棵樹:
7
/ \
4 8
/ \
2 6
\ /
3 5
如果要刪除結點4,那么應該將結點5補到4的位置,這樣才能保證還是 BST,那么結果是如下這棵樹:
7
/ \
5 8
/ \
2 6
\
3
先來看一種遞歸的解法,首先判斷根節點是否為空。由於 BST 的左<根<右的性質,使得可以快速定位到要刪除的結點,對於當前結點值不等於 key 的情況,根據大小關系對其左右子結點分別調用遞歸函數。若當前結點就是要刪除的結點,先判斷若有一個子結點不存在,就將 root 指向另一個結點,如果左右子結點都不存在,那么 root 就賦值為空了,也正確。難點就在於處理左右子結點都存在的情況,需要在右子樹找到最小值,即右子樹中最左下方的結點,然后將該最小值賦值給 root,然后再在右子樹中調用遞歸函數來刪除這個值最小的結點,參見代碼如下:
解法一:
class Solution { public: TreeNode* deleteNode(TreeNode* root, int key) { if (!root) return NULL; if (root->val > key) { root->left = deleteNode(root->left, key); } else if (root->val < key) { root->right = deleteNode(root->right, key); } else { if (!root->left || !root->right) { root = (root->left) ? root->left : root->right; } else { TreeNode *cur = root->right; while (cur->left) cur = cur->left; root->val = cur->val; root->right = deleteNode(root->right, cur->val); } } return root; } };
下面來看迭代的寫法,還是通過 BST 的性質來快速定位要刪除的結點,如果沒找到直接返回空。遍歷的過程要記錄上一個位置的結點 pre,如果 pre 不存在,說明要刪除的是根結點,如果要刪除的結點在 pre 的左子樹中,那么 pre 的左子結點連上刪除后的結點,反之 pre 的右子結點連上刪除后的結點。在刪除函數中,首先判空,若為空,直接返回空指針;否則檢測若右子結點不存在,直接返回左子結點即可,因為沒有右子樹就不會牽扯到調整樹結構的問題;若右子結點存在,需要找到右子樹中的最小值,即右子樹中的最左子結點,用一個 while 循環找到即可,然后將要刪除結點的左子結點連到右子樹的最左子結點的左子結點上即可(說的有點繞,大家仔細體會一下),最后返回要刪除結點的右子結點即可,文字表述確實比較繞,請大家自行帶例子一步一步觀察就會很清晰明了,參見代碼如下:
解法二:
class Solution { public: TreeNode* deleteNode(TreeNode* root, int key) { if (!root) return nullptr; TreeNode *cur = root, *pre = nullptr; while (cur) { if (cur->val == key) break; pre = cur; if (cur->val > key) cur = cur->left; else cur = cur->right; } if (!pre) return del(cur); if (pre->left && pre->left->val == key) pre->left = del(cur); else pre->right = del(cur); return root; } TreeNode* del(TreeNode* node) { if (!node) return nullptr; if (!node->right) return node->left; TreeNode *t = node->right; while (t->left) t = t->left; t->left = node->left; return node->right; } };
下面來看一種對於二叉樹通用的解法,適用於所有二叉樹,所以並沒有利用 BST 的性質,而是遍歷了所有的結點,然后刪掉和 key 值相同的結點,參見代碼如下:
解法三:
class Solution { public: TreeNode* deleteNode(TreeNode* root, int key) { if (!root) return NULL; if (root->val == key) { if (!root->right) return root->left; else { TreeNode *cur = root->right; while (cur->left) cur = cur->left; swap(root->val, cur->val); } } root->left = deleteNode(root->left, key); root->right = deleteNode(root->right, key); return root; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/450
類似題目:
參考資料:
https://leetcode.com/problems/delete-node-in-a-bst/