给定一个二叉搜索树的根节点 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
思想:
这道题用递归的思想,先利用bst的左<根<右的性质,找到key=root->val对应的节点。找节点步骤如下:
如果root->val<key,说明待查找节点在左子树,则返回左子树修改后的结果。
如果root->val>key,说明待查找节点在右子树,则返回右子树修改后的结果。
找到节点后,根据以下步骤删除节点:
如果左子树或右子树为空,则返回非空的那个子树,如果左右子树都不为空,则找到右子树最小(左)的节点,把该节点替换到当前节点,并递归调用函数删除这个节点。
代码如下:
TreeNode* deleteNode(TreeNode* root, int key) { if (!root) { return nullptr; } if (key < root->val) { root->left = deleteNode(root->left, key); } if (key > root->val) { root->right = deleteNode(root->right, key); } if (key == root->val) { if (!root->left || !root->right) { root = root->left ? root->left : root->right; } else { TreeNode* tmp = root->right; while (tmp->left) { tmp = tmp->left; } root->val = tmp->val; root->right = deleteNode(root->right, tmp->val); } } return root; }
改进算法:
有同学指出没有必要在交换了待删除节点和右节点的最小节点后再递归调用函数删除右节点的最小节点,可以直接把待删除节点的左节点赋值为右节点的左节点即可,从而提高效率。
代码:
TreeNode* deleteNode(TreeNode* root, int key) { if (!root) { return nullptr; } if (key < root->val) { root->left = deleteNode(root->left, key); } if (key > root->val) { root->right = deleteNode(root->right, key); } if (key == root->val) { if (!root->left || !root->right) { root = root->left ? root->left : root->right; } else { TreeNode* tmp = root->right; while (tmp->left) { tmp = tmp->left; } tmp->left = root->left; return root->right; } } return root; }