在前一篇文章中,讨论了二叉查找树的查找以及插入操作。本篇文章主要讨论删除操作。
当删除一个节点时,可能包含下面的这些情形:
1) 被删节点为叶子:
这种情况下,简单的将叶子删除即可。
50 50
/ \ delete(20) / \
30 70 ------------------> 30 70
/ \ / \ \ / \
20 40 60 80 40 60 80
2) 被删节点只有一个孩子:
将孩子节点拷贝到被删节点,并删除孩子节点。
50 50
/ \ delete(30) / \
30 70 ------------------> 40 70
\ / \ / \
40 60 80 60 80
3) 被删节点有两个孩子:
进行中序遍历,找到被删节点的后继节点。将后继节点拷贝到被删节点,然后删除后继节点。
注意:也可以利用中序遍历中的前驱节点来实现这个操作。
50 60
/ \ delete(50) / \
40 70 -----------------> 40 70
/ \ \
60 80 80
需要注意的一个重点是:仅当右孩子不为空时,才需要后继节点。在这种特定情况下,可以通过查找右子树的最小值来获取到后继节点。
代码实现:
#include <iostream>
struct Node
{
int key;
Node *left;
Node *right;
};
Node * minValueNode(Node* node)
{
Node* current = node;
//查找最左侧的叶子
while (current->left != NULL)
current = current->left;
return current;
}
//删除二叉搜索树的一个指定节点,并返回新的根节点
Node* deleteNode(Node* root, int key)
{
// base case
if (root == NULL)
return root;
//如果指定值小于根节点值,则这个值处于左子树中。
if (key < root->key)
root->left = deleteNode(root->left, key);
//如果指定值大于根节点值,则这个值处于右子树中。
else if (key > root->key)
root->right = deleteNode(root->right, key);
//找到了匹配的节点
else
{
// 此节点可能有孩子,也可能没有.
// 这里处理只有一个孩子,或者没有孩子的情况
if (root->left == NULL)
{
Node *temp = root->right;
delete root;
return temp;
}
else if (root->right == NULL)
{
Node *temp = root->left;
delete root;
return temp;
}
// 处理有两个孩子的情况: 取得中序遍历中当前节点的后继节点,即右子树中的最小值(最左侧的叶子)。
Node* temp = minValueNode(root->right);
// 将后继节点中的值替换掉匹配节点中的值
root->key = temp->key;
// 删除后继节点
root->right = deleteNode(root->right, temp->key);
}
return root;
}
// 创建一个新的BST节点
Node *createNewNode(int item)
{
Node *temp = new Node;
temp->key = item;
temp->left = temp->right = NULL;
return temp;
}
// 中序遍历二叉搜索树
void inorder(Node *root)
{
if (root != NULL)
{
inorder(root->left);
std::cout << " " << root->key << " ";
inorder(root->right);
}
}
//插入新节点至二叉搜索树中
Node* insert(Node* node, int key)
{
//空树
if (node == NULL)
return createNewNode(key);
//递归插入。如果已存在指定值,则不插入
if (key < node->key)
node->left = insert(node->left, key);
else if (key > node->key)
node->right = insert(node->right, key);
//返回未修改的node指针
return node;
}
int main()
{
/* 构建一颗如下所示的BST
50
/ \
30 70
/ \ / \
20 40 60 80
*/
Node *root = NULL;
root = insert(root, 50);
insert(root, 30);
insert(root, 20);
insert(root, 40);
insert(root, 70);
insert(root, 60);
insert(root, 80);
std::cout << "Inorder traversal of the given tree \n";
inorder(root);
std::cout << "\nDelete 20\n";
root = deleteNode(root, 20);
std::cout << "Inorder traversal of the modified tree \n";
inorder(root);
std::cout << "\nDelete 30\n";
root = deleteNode(root, 30);
std::cout << "Inorder traversal of the modified tree \n";
inorder(root);
std::cout << "\nDelete 50\n";
root = deleteNode(root, 50);
std::cout << "Inorder traversal of the modified tree \n";
inorder(root);
return 0;
}
输出:
Inorder traversal of the given tree
20 30 40 50 60 70 80
Delete 20
Inorder traversal of the modified tree
30 40 50 60 70 80
Delete 30
Inorder traversal of the modified tree
40 50 60 70 80
Delete 50
Inorder traversal of the modified tree
40 60 70 80
时间复杂度: 删除操作的最坏时间复杂度为O(h),其中h代表树的高度。在最坏情况下,需要从根节点遍历到最深的叶子节点。