给定一个二叉搜索树的根节点 root 和一个值 key ,删除二叉搜索树中的 **key **对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
示例 1:
**输入:** root = [5,3,6,2,4,null,7], key = 3
**输出:** [5,4,6,2,null,null,7]
**解释:** 给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
另一个正确答案是 [5,2,6,null,4,null,7]。
![](https://assets.leetcode.com/uploads/2020/09/04/del_node_supp.jpg)
示例 2:
**输入:** root = [5,3,6,2,4,null,7], key = 0
**输出:** [5,3,6,2,4,null,7]
**解释:** 二叉树不包含值为 0 的节点
示例 3:
**输入:** root = [], key = 0
**输出:** []
提示:
节点数的范围 [0, 104]
.
-105 <= Node.val <= 105
节点值唯一
root
是合法的二叉搜索树
-105 <= key <= 105
进阶: 要求算法时间复杂度为 O(h),h 为树的高度。
方法一:递归 思路
二叉搜索树有以下性质:
左子树的所有节点(如果有)的值均小于当前节点的值;
右子树的所有节点(如果有)的值均大于当前节点的值;
左子树和右子树均为二叉搜索树。
二叉搜索树的题目往往可以用递归来解决。此题要求删除二叉树的节点,函数 deleteNode 的输入是二叉树的根节点 root 和一个整数 key,输出是删除值为 key 的节点后的二叉树,并保持二叉树的有序性。可以按照以下情况分类讨论:
root 为空,代表未搜索到值为 key 的节点,返回空。
root.val} > \textit{key,表示值为 key 的节点可能存在于 root 的左子树中,需要递归地在 root.left 调用 deleteNode,并返回 root。
root.val} < \textit{key,表示值为 key 的节点可能存在于 root 的右子树中,需要递归地在 root.right 调用 deleteNode,并返回 root。
root.val} = \textit{key,root 即为要删除的节点。此时要做的是删除 root,并将它的子树合并成一棵子树,保持有序性,并返回根节点。根据 root 的子树情况分成以下情况讨论:
root 为叶子节点,没有子树。此时可以直接将它删除,即返回空。
root 只有左子树,没有右子树。此时可以将它的左子树作为新的子树,返回它的左子节点。
root 只有右子树,没有左子树。此时可以将它的右子树作为新的子树,返回它的右子节点。
root 有左右子树,这时可以将 root 的后继节点(比 root 大的最小节点,即它的右子树中的最小节点,记为 successor)作为新的根节点替代 root,并将 successor 从 root 的右子树中删除,使得在保持有序性的情况下合并左右子树。 简单证明,successor 位于 root 的右子树中,因此大于 root 的所有左子节点;successor 是 root 的右子树中的最小节点,因此小于 root 的右子树中的其他节点。以上两点保持了新子树的有序性。 在代码实现上,我们可以先寻找 successor,再删除它。successor 是 root 的右子树中的最小节点,可以先找到 root 的右子节点,再不停地往左子节点寻找,直到找到一个不存在左子节点的节点,这个节点即为 successor。然后递归地在 root.right 调用 deleteNode 来删除 successor。因为 successor 没有左子节点,因此这一步递归调用不会再次步入这一种情况。然后将 successor 更新为新的 root 并返回。
代码
[sol1-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution : def deleteNode (self, root: Optional [TreeNode], key: int ) -> Optional [TreeNode]: if root is None : return None if root.val > key: root.left = self.deleteNode(root.left, key) elif root.val < key: root.right = self.deleteNode(root.right, key) elif root.left is None or root.right is None : root = root.left if root.left else root.right else : successor = root.right while successor.left: successor = successor.left successor.right = self.deleteNode(root.right, successor.val) successor.left = root.left return successor return root
[sol1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution {public : TreeNode* deleteNode (TreeNode* root, int key) { if (root == nullptr ) { return nullptr ; } if (root->val > key) { root->left = deleteNode (root->left, key); return root; } if (root->val < key) { root->right = deleteNode (root->right, key); return root; } if (root->val == key) { if (!root->left && !root->right) { return nullptr ; } if (!root->right) { return root->left; } if (!root->left) { return root->right; } TreeNode *successor = root->right; while (successor->left) { successor = successor->left; } root->right = deleteNode (root->right, successor->val); successor->right = root->right; successor->left = root->left; return successor; } return root; } };
[sol1-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution { public TreeNode deleteNode (TreeNode root, int key) { if (root == null ) { return null ; } if (root.val > key) { root.left = deleteNode(root.left, key); return root; } if (root.val < key) { root.right = deleteNode(root.right, key); return root; } if (root.val == key) { if (root.left == null && root.right == null ) { return null ; } if (root.right == null ) { return root.left; } if (root.left == null ) { return root.right; } TreeNode successor = root.right; while (successor.left != null ) { successor = successor.left; } root.right = deleteNode(root.right, successor.val); successor.right = root.right; successor.left = root.left; return successor; } return root; } }
[sol1-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class Solution { public TreeNode DeleteNode (TreeNode root, int key ) { if (root == null ) { return null ; } if (root.val > key) { root.left = DeleteNode(root.left, key); return root; } if (root.val < key) { root.right = DeleteNode(root.right, key); return root; } if (root.val == key) { if (root.left == null && root.right == null ) { return null ; } if (root.right == null ) { return root.left; } if (root.left == null ) { return root.right; } TreeNode successor = root.right; while (successor.left != null ) { successor = successor.left; } root.right = DeleteNode(root.right, successor.val); successor.right = root.right; successor.left = root.left; return successor; } return root; } }
[sol1-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 struct TreeNode* deleteNode (struct TreeNode* root, int key) { if (root == NULL ) { return NULL ; } if (root->val > key) { root->left = deleteNode(root->left, key); return root; } if (root->val < key) { root->right = deleteNode(root->right, key); return root; } if (root->val == key) { if (!root->left && !root->right) { return NULL ; } if (!root->right) { return root->left; } if (!root->left) { return root->right; } struct TreeNode *successor = root->right; while (successor->left) { successor = successor->left; } root->right = deleteNode(root->right, successor->val); successor->right = root->right; successor->left = root->left; return successor; } return root; }
[sol1-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func deleteNode (root *TreeNode, key int ) *TreeNode { switch { case root == nil : return nil case root.Val > key: root.Left = deleteNode(root.Left, key) case root.Val < key: root.Right = deleteNode(root.Right, key) case root.Left == nil || root.Right == nil : if root.Left != nil { return root.Left } return root.Right default : successor := root.Right for successor.Left != nil { successor = successor.Left } successor.Right = deleteNode(root.Right, successor.Val) successor.Left = root.Left return successor } return root }
[sol1-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 var deleteNode = function (root, key ) { if (!root) { return null ; } if (root.val > key) { root.left = deleteNode (root.left , key); return root; } if (root.val < key) { root.right = deleteNode (root.right , key); return root; } if (root.val === key) { if (!root.left && !root.right ) { return null ; } if (!root.right ) { return root.left ; } if (!root.left ) { return root.right ; } let successor = root.right ; while (successor.left ) { successor = successor.left ; } root.right = deleteNode (root.right , successor.val ); successor.right = root.right ; successor.left = root.left ; return successor; } return root; };
复杂度分析
方法二:迭代 思路
方法一的递归深度最多为 $n$,而大部分是由寻找值为 key 的节点贡献的,而寻找节点这一部分可以用迭代来优化。寻找并删除 successor 时,也可以用一个变量保存它的父节点,从而可以节省一步递归操作。
代码
[sol2-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution : def deleteNode (self, root: Optional [TreeNode], key: int ) -> Optional [TreeNode]: cur, curParent = root, None while cur and cur.val != key: curParent = cur cur = cur.left if cur.val > key else cur.right if cur is None : return root if cur.left is None and cur.right is None : cur = None elif cur.right is None : cur = cur.left elif cur.left is None : cur = cur.right else : successor, successorParent = cur.right, cur while successor.left: successorParent = successor successor = successor.left if successorParent.val == cur.val: successorParent.right = successor.right else : successorParent.left = successor.right successor.right = cur.right successor.left = cur.left cur = successor if curParent is None : return cur if curParent.left and curParent.left.val == key: curParent.left = cur else : curParent.right = cur return root
[sol2-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Solution {public : TreeNode* deleteNode (TreeNode* root, int key) { TreeNode *cur = root, *curParent = nullptr ; while (cur && cur->val != key) { curParent = cur; if (cur->val > key) { cur = cur->left; } else { cur = cur->right; } } if (!cur) { return root; } if (!cur->left && !cur->right) { cur = nullptr ; } else if (!cur->right) { cur = cur->left; } else if (!cur->left) { cur = cur->right; } else { TreeNode *successor = cur->right, *successorParent = cur; while (successor->left) { successorParent = successor; successor = successor->left; } if (successorParent->val == cur->val) { successorParent->right = successor->right; } else { successorParent->left = successor->right; } successor->right = cur->right; successor->left = cur->left; cur = successor; } if (!curParent) { return cur; } else { if (curParent->left && curParent->left->val == key) { curParent->left = cur; } else { curParent->right = cur; } return root; } } };
[sol2-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Solution { public TreeNode deleteNode (TreeNode root, int key) { TreeNode cur = root, curParent = null ; while (cur != null && cur.val != key) { curParent = cur; if (cur.val > key) { cur = cur.left; } else { cur = cur.right; } } if (cur == null ) { return root; } if (cur.left == null && cur.right == null ) { cur = null ; } else if (cur.right == null ) { cur = cur.left; } else if (cur.left == null ) { cur = cur.right; } else { TreeNode successor = cur.right, successorParent = cur; while (successor.left != null ) { successorParent = successor; successor = successor.left; } if (successorParent.val == cur.val) { successorParent.right = successor.right; } else { successorParent.left = successor.right; } successor.right = cur.right; successor.left = cur.left; cur = successor; } if (curParent == null ) { return cur; } else { if (curParent.left != null && curParent.left.val == key) { curParent.left = cur; } else { curParent.right = cur; } return root; } } }
[sol2-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class Solution { public TreeNode DeleteNode (TreeNode root, int key ) { TreeNode cur = root, curParent = null ; while (cur != null && cur.val != key) { curParent = cur; if (cur.val > key) { cur = cur.left; } else { cur = cur.right; } } if (cur == null ) { return root; } if (cur.left == null && cur.right == null ) { cur = null ; } else if (cur.right == null ) { cur = cur.left; } else if (cur.left == null ) { cur = cur.right; } else { TreeNode successor = cur.right, successorParent = cur; while (successor.left != null ) { successorParent = successor; successor = successor.left; } if (successorParent.val == cur.val) { successorParent.right = successor.right; } else { successorParent.left = successor.right; } successor.right = cur.right; successor.left = cur.left; cur = successor; } if (curParent == null ) { return cur; } else { if (curParent.left != null && curParent.left.val == key) { curParent.left = cur; } else { curParent.right = cur; } return root; } } }
[sol2-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 struct TreeNode* deleteNode (struct TreeNode* root, int key) { struct TreeNode *cur = root, *curParent = NULL ; while (cur && cur->val != key) { curParent = cur; if (cur->val > key) { cur = cur->left; } else { cur = cur->right; } } if (!cur) { return root; } if (!cur->left && !cur->right) { cur = NULL ; } else if (!cur->right) { cur = cur->left; } else if (!cur->left) { cur = cur->right; } else { struct TreeNode *successor = cur->right, *successorParent = cur; while (successor->left) { successorParent = successor; successor = successor->left; } if (successorParent->val == cur->val) { successorParent->right = successor->right; } else { successorParent->left = successor->right; } successor->right = cur->right; successor->left = cur->left; cur = successor; } if (!curParent) { return cur; } else { if (curParent->left && curParent->left->val == key) { curParent->left = cur; } else { curParent->right = cur; } return root; } }
[sol2-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 func deleteNode (root *TreeNode, key int ) *TreeNode { var cur, curParent *TreeNode = root, nil for cur != nil && cur.Val != key { curParent = cur if cur.Val > key { cur = cur.Left } else { cur = cur.Right } } if cur == nil { return root } if cur.Left == nil && cur.Right == nil { cur = nil } else if cur.Right == nil { cur = cur.Left } else if cur.Left == nil { cur = cur.Right } else { successor, successorParent := cur.Right, cur for successor.Left != nil { successorParent = successor successor = successor.Left } if successorParent.Val == cur.Val { successorParent.Right = successor.Right } else { successorParent.Left = successor.Right } successor.Right = cur.Right successor.Left = cur.Left cur = successor } if curParent == nil { return cur } if curParent.Left != nil && curParent.Left.Val == key { curParent.Left = cur } else { curParent.Right = cur } return root }
[sol2-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 var deleteNode = function (root, key ) { let cur = root, curParent = null ; while (cur && cur.val !== key) { curParent = cur; if (cur.val > key) { cur = cur.left ; } else { cur = cur.right ; } } if (!cur) { return root; } if (!cur.left && !cur.right ) { cur = null ; } else if (!cur.right ) { cur = cur.left ; } else if (!cur.left ) { cur = cur.right ; } else { let successor = cur.right , successorParent = cur; while (successor.left ) { successorParent = successor; successor = successor.left ; } if (successorParent.val === cur.val ) { successorParent.right = successor.right ; } else { successorParent.left = successor.right ; } successor.right = cur.right ; successor.left = cur.left ; cur = successor; } if (!curParent) { return cur; } else { if (curParent.left && curParent.left .val === key) { curParent.left = cur; } else { curParent.right = cur; } return root; } };
复杂度分析