题目:
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
// /********************* 法一:BST 递归法*******************************/
// public:
// TreeNode* deleteNode(TreeNode* root, int key)
// {
// // 终止条件,也是第1种情况,如果遇到了空节点,说明没找到,那就直接返回咯
// if(root ==nullptr)
// {
// return root;
// }
// // 当找到对应节点时,又分了4种情况
// if(root->val == key)
// {
// // 第二种情况,左右孩子都为空,直接删除节点,返回 nullptr为根节点
// // 第三种情况,左孩子为空,右孩子不为空,直接删除节点,右孩子补位
// // 第四种情况,右孩子为空,左孩子不为空,直接删除节点,左孩子补位
// // 注意::第二种情况可以和第三或者第四合并一下
// if(root->left == nullptr) // 合并了第二种情况
// {
// return root->right;
// }
// else if(root->right == nullptr)
// {
// return root->left;
// }
// // 第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到
// // 删除节点的右子树的最左面节点的左孩子的位置,并返回删除节点右孩子为新的根节点
// else
// {
// TreeNode* cur = root->right; // 定义一个指针指向右子树,找右子树最左面的节点
// while(cur->left != nullptr)
// {
// // 找到右子树最左面的节点
// cur = cur->left;
// }
// cur->left = root->left; // 把要删除的节点的左子树放在上面找到的 cur 的左孩子的位置
// TreeNode* temp = root; // 把root节点临时操作一下,
// root = root->right; // 用root->right 覆盖root
// delete temp; // 释放删除的节点的内存
// return root;
// }
// }
// if(root->val > key)
// {
// root->left = deleteNode(root->left, key);
// }
// if(root->val < key)
// {
// root->right = deleteNode(root->right, key);
// }
// return root;
// }
// /************************法二:普通二叉树的 递归法****************************/
// // 普通二叉树的删除方式(没有使用搜索树的特性,遍历整棵树),用交换值的操作来删除目标节点。
// // 但是这种写法我不太能理解,也不是不太能,就是不能吧~ 覆盖?????emmmmmm饿了
// public:
// TreeNode* deleteNode(TreeNode* root, int key)
// {
// if(root == nullptr)
// {
// return root;
// }
// if(root->val == key)
// {
// // 这里第二次操作目标值: 最终删除的作用
// if(root->right == nullptr)
// {
// return root->left;
// }
// 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;
// }
/***********************法三:迭代法*************************/
// 注意:BST 的迭代法都不用栈或者队列哦
// 模拟递归法中的逻辑来删除节点,但需要一个pre记录cur的父节点,方便做删除操作。
private:
// 将目标节点(也就是要删除的节点)的左子树放到 目标节点的右子树的最左面节点的左孩子的位置上
// 并返回目标节点右孩子为新的根节点
TreeNode* deleteOneNode(TreeNode* target)
{
// 需要被删除节点为 空节点
if(target == nullptr)
{
return target;
}
// 需要被删除节点的右子树为空节点
if(target->right == nullptr)
{
return target->left;
}
TreeNode* cur = target->right;
while(cur->left)
{
cur = cur->left;
}
cur->left = target->left;
return target->right;
}
public:
TreeNode* deleteNode(TreeNode* root, int key)
{
if(root == nullptr)
{
return root;
}
TreeNode* cur = root;
TreeNode* pre = nullptr; // 记录cur的父节点,用来删除 cur
// 通过while循环找到要删除的节点 cur
while(cur)
{
if(cur->val == key)
{
break;
}
pre = cur;
if(cur->val > key)
{
cur = cur->left;
}
else
{
cur = cur->right;
}
}
// pre == nullptr 说明根本没进 while 循环,即该树只有头结点
if(pre == nullptr)
{
return deleteOneNode(cur);
}
if(pre->left != nullptr && pre->left->val == key)
{
pre->left = deleteOneNode(cur);
}
if(pre->right != nullptr && pre->right->val == key)
{
pre->right =deleteOneNode(cur);
}
return root;
}
};