查找 -- 红黑树的删除操作
来源:互联网 发布:电脑魔方计时器软件 编辑:程序博客网 时间:2024/05/22 16:56
在红黑树里面最复杂的操作就是对红黑树里面的节点进行删除了。不仅要进行二叉搜索树的删除操作,还需要进行对红黑树性质的保持。
我们先定义删除操作的函数原型:
void RBTree<Key, Value>::erase(Key key)
函数接受一个Key类型的值,树会搜索含有这个值的节点,并且删除这个节点,如果找不到,不会对树进行任何的操作。
删除操作也需要一些辅助函数:
void rotateLeft(Node<Key, Value>* node);void rotateRight(Node<Key, Value>* node);Node<Key, Value>* findNext(Node<Key, Value>* node);Node<Key, Value>* findMin(Node<Key, Value>* node);void dealOneChild(Node<Key, Value>* node, Node<Key, Value>* child);void adjustDelete(Node<Key, Value>* node, Node<Key, Value>* realParent);
上面的函数中我们需要关注的函数只有adjustDelete这个函数,这个是用来在删除一个节点之后,用来调整红黑树的。
其他的操作依次为:①左旋 ②右旋 ③找到node节点中序的下一个节点 ④找到以node为根的树的最小节点 ⑤交换两个节点的位置(指针操作,并不是交换节点内部的成员)
我们可以想象:删除一个节点会出现四种情况:删除的节点(del)替代的节点(rep)
① del是红色的,rep是红色的。
② del是红色的,rep是黑色的。
③ del是黑色的,rep是红色的。
④ del是黑色的,rep是黑色的。
当rep是红色的时候,无论del是红是黑,只要简单地把rep的颜色改成黑色就可以保持所有的红黑树性质。
所以我们只需要对rep是黑色的那两种情况进行讨论即可。
在二叉搜索树中,我们删除一个节点会分成三种情况:①被删除节点的左子女为空 ②被删除节点的右子女为空 ③被删除的节点左右子女都不为空。
同样的,我们在红黑树的删除中,也需要考虑这三种情况,并且要对这三种情况进行相应的处理(因为涉及到一些指针的保存)
删除操作跟插入操作不一样,不是对于父节点的兄弟节点进行判断,而是对于rep的兄弟节点来进行判断,因为rep是黑色的,所以在rep到它祖先节点的所有节点上,第五条规则都会被破坏掉,我们需要对rep的兄弟节点的颜色来进行不同的处理。(这些处理都是对于第五条规则的维护)
注意:这里的rep是已经把del替换之后的rep,而不是替换之前的rep
同样的,我们也需要对bro的左右进行判断,并且左右也是对称的,跟插入一样。
①当rep的兄弟节点 (bro)为红色的时候:
我们可以通过把bro的颜色变黑,把rep和bro的父节点变红,进行一次旋转。
②当rep的兄弟节点为黑色的时候:
在这种情况中,我们还需要对bro的子女的颜色进行判断:双黑,左边红色,右边红色。
(1)双黑:因为rep那边已经少了一个黑色节点,我们需要把bro变成红色,达到平衡效果
(2)左边红色:我们需要把bro的左子女变成黑色,bro变成红色,进行一次旋转变成(3)这种情况
(3)右边红色:bro变成红色,rep的父节点变成黑色,进行一次旋转。
经过最多三次变化,我们就可以维持住红黑性质。
下面是主要的代码(adjustDelete,erase)
erase:
template <class Key, class Value>void RBTree<Key, Value>::erase(Key key) { Node<Key, Value>* target = find(key); if (target == NULL) { cout << "Can not delete node not exist" << endl; return ; } int originalColor = target->color; //next节点是用来替换被删除的节点的,replaceNode是用来替换next节点的 //我们还需要一个额外的节点来保存replaceNode的父节点,因为replaceNode的值可能是NULL //额外的节点也要当做参数传到adjustDelete函数里面 Node<Key, Value>* next = target; Node<Key, Value>* replaceNode = NULL; Node<Key, Value>* replaceParent = NULL; // 先处理要删除的节点只有一个子女的情况 if (target->left == NULL) { replaceNode = target->right; dealOneChild(target, replaceNode); replaceParent = target->parent; } else if (target->right == NULL) { replaceNode = target->left; dealOneChild(target, replaceNode); replaceParent = target->parent; } else { //这里开始处理删除节点有两个子女的情况 next = findNext(target); originalColor = next->color; replaceNode = next->right; //只能是右子女,左子女的话,那么就不是最小的节点了 //要处理next的右子树的根不为后继的情况,这里主要是处理后继节点替换 //如果右子树的根为后继的话,那么直接就开始替换被删除的节点了 if (next->parent != target) { dealOneChild(next, next->right); next->right = target->right; target->right->parent = next; replaceParent = next->parent; //注意这个赋值 } else { replaceParent = next; } dealOneChild(target, next); next->left = target->left; target->left->parent = next; next->color = target->color; } if (originalColor == BLACK) { adjustDelete(replaceNode, replaceParent); } delete target; target = NULL;}
adjustDelete:
template <class Key, class Value>void RBTree<Key, Value>::adjustDelete(Node<Key, Value>* node, Node<Key, Value>* nodeParent) { while (node != root && (node == NULL || node->color == BLACK)) { if (node == nodeParent->left) { Node<Key, Value>* bro = nodeParent->right; if (bro->color == RED) { //node的兄弟节点为红色,情况1 bro->color = BLACK; bro->parent->color = RED; rotateLeft(bro->parent); bro = nodeParent->right; } //node兄弟节点为黑色 if ((bro->left == NULL || bro->left->color == BLACK) && (bro->right == NULL || bro->right->color == BLACK)) { //兄弟节点的两个子节点都为黑色,情况2 bro->color = RED; node = nodeParent; nodeParent = nodeParent->parent; } else { //兄弟节点其中一个节点为红色 if (bro->right == NULL || bro->right->color == BLACK) { //兄弟节点的左子女为红色,情况3 if (bro->left != NULL) { bro->left->color = BLACK; } bro->color = RED; rotateRight(bro); bro = nodeParent->right; } // 兄弟节点的右子女为红色,情况4 bro->color = RED; nodeParent->color = BLACK; if (bro->right != NULL) { bro->right->color = BLACK; } rotateLeft(nodeParent); node = root; } } else { Node<Key, Value>* bro = nodeParent->left; if (bro->color == RED) { bro->color = BLACK; bro->parent->color = RED; rotateRight(bro->parent); bro = nodeParent->left; } if ((bro->left == NULL || bro->left->color == BLACK) && (bro->right == NULL || bro->right->color == BLACK)) { bro->color = RED; node = nodeParent; nodeParent = nodeParent->parent; } else { if (bro->left == NULL || bro->left->color == BLACK) { if (bro->right != NULL) { bro->right->color = BLACK; } bro->color = RED; rotateLeft(bro); bro = nodeParent->left; } bro->color = RED; nodeParent->color = BLACK; if (bro->left != NULL) { bro->left->color = BLACK; } rotateRight(nodeParent); node = root; } } } if (node != NULL) { node->color = BLACK; }}
上面的代码中,需要注意的是replaceParent这个指针变量,因为我们在寻找到next之后,next->right这个值可能是null,所以不能简单地通过next->right->parent这个值来找到真正的next的父节点,所以对不同的情况我们需要分开赋值(在替换节点的时候)
- 查找 -- 红黑树的删除操作
- 二叉查找树的删除操作
- 查找二叉树删除节点的操作
- 二叉查找树的删除操作
- BST的插入、删除、查找操作
- AVL树的插入、删除、查找操作
- 二叉查找树的操作(插入、删除、查找)
- 二叉查找树的基本操作之查找插入删除
- 二叉查找树的插入、删除、查找操作
- 二叉查找树删除操作
- 单链表操作---建表,查找,删除
- 单链表的创建、插入,删除、查找等操作
- 二叉搜索树的建立, 查找, 删除操作...
- 简单二叉排序树/二叉查找树的构建插入删除操作
- 单向链表的操作:创建,删除,插入,销毁,查找
- 顺序表的建立、插入、删除、查找、遍历操作
- 动态链表的删除 插入 查找操作
- 二叉排序树的操作(建立、插入、删除和查找)
- java -xml文件读取
- mysql左连接右连接(查询两张表不同的数据)
- tesseract 遇到的一些问题
- 开启子线程的方法(二)
- JUnit 实现多并发
- 查找 -- 红黑树的删除操作
- 001-js基础-变量提升练习题
- wx.toast 页面跳转延时的问题
- TCP/IP 小结
- 打开谷歌浏览器(chrome)线程lsass.exe的cpu占用高的解决办法
- <s:iterator>和<s:if>结合使用
- 【BzoJ 3319】【黑白树】【并查集】【”猥琐欲为“】
- java学习笔记-初探字节码和命令执行效率
- 挂载光盘