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的右子节点连上删除后的节点。在删除函数中,如果左右子节点都不存在,那么返回空;如果有一个不存在,那么我们返回那个存在的;难点还是在于处理左右子节点都存在的情况,还是要找到需要删除节点的右子树中的最小值,然后把最小值赋值给要删除节点,然后就是要处理最小值可能存在的右子树的连接问题,如果要删除节点的右子节点没有左子节点了的话,那么最小值的右子树直接连到要删除节点的右子节点上即可(因为此时原本要删除的节点的值已经被最小值替换了,所以现在其实是要删掉最小值节点)。否则我们就把最小值节点的右子树连到其父节点的左子节点上。文字表述确实比较绕,请大家自行带例子一步一步观察就会很清晰明了了,参见代码如下:
解法二:
class Solution { public: TreeNode* deleteNode(TreeNode* root, int key) { TreeNode *cur = root, *pre = NULL; while (cur) { if (cur->val == key) break; pre = cur; if (cur->val > key) cur = cur->left; else cur = cur->right; } if (!cur) return root; 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->left && !node->right) return NULL; if (!node->left || !node->right) { return (node->left) ? node->left : node->right; } TreeNode *pre = node, *cur = node->right; while (cur->left) { pre = cur; cur = cur->left; } node->val = cur->val; (pre == node ? node->right : pre->left) = cur->right; return node; } };
下面来看一种对于二叉树通用的解法,适用于所有二叉树,所以并没有利用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; } };
参考资料:
https://discuss.leetcode.com/topic/65792/recursive-easy-to-understand-java-solution/2