红黑树总览
一. 规则
1. 根节点必须是黑色;
2. 节点要么是黑色要么是红色;
3. 叶子节点都为黑色;
4. 从任意节点出发到根节点,所经历的黑色节点的数目相同
5. 如果节点为红,其子节点必须为黑
二.插入
1.当前节点是根节点:把根节点改为黑色
2.当前节点的父节点是黑节点,保持不变
3.当前节点的父节点是红节点,并且当前接电费的叔叔节点是红色:把父节点和叔叔节点改为黑色,把祖父节点改为红色,把祖父节点作为当前节点,向上判断
4.当前节点的父节点是红节点,并且当前叔叔节点不是红节点:旋转,可以分为两种情况,即外侧插入和内侧插入
外侧插入进行左旋或者右旋,内侧插入则需要进行双旋转,可以细分为一下四种情况:
a). 祖父节点 -> 父节点 -> 当前节点, 一直向右:做左旋,再把父节点改为黑色,之前的祖父节点改为红色
b). 祖父节点 -> 父节点 -> 当前节点,一直向左:做右旋,再把父节点改为黑色,之前的祖父节点改为红色
c). 祖父节点 -> 父节点 -> 当前节点,先向左再向右:先对当前节点做左旋,再对当前节点做右旋,然后把当前节点改为黑色,之前的祖父节点改为红色
d). 祖父节点 -> 父节点 -> 当前节点,先向右再向左: 先对当前节点做右旋,再对当前节点做左旋,然后把当前节点改为黑色,之前祖父节点改为红色
inline void _Rb_tree_rebalance(_Rb_tree_node_base* _x, _Rb_tree_node_base* _root) { // 新节点必为红 _x -> _M_color = _S_rb_tree_red; // 插入节点的父亲节点是红色 while (_x != _root && _x -> _M_parent -> _M_color == _S_rb_tree_red) { // 父亲节点为祖父节点的左孩子节点 if (_x -> _M_parent == _x -> _M_parent -> _M_parent -> _M_left) { // 令y为节点,作为祖父节点的右孩子节点 _Rb_tree_node_base* _y = _x -> _M_parent -> _M_parent -> _M_right; // 叔叔节点存在,且为红色 if (_y && _y -> _M_color == _S_rb_tree_red) { // 将父亲节点的颜色改为黑色 _x -> _M_parent -> _M_color = _S_rb_tree_black; // 叔叔节点改为黑色 _y -> _M_color = _S_rb_tree_black; // 将祖父节点的颜色改为红色 _x -> _M_parent -> _M_parent -> _M_color = _S_rb_tree_red; // 将插入节点指向祖父节点 _x = _x -> _M_parent -> _M_parent; } else { // 无叔叔节点, 或者叔叔节点为黑色,左旋转操作 if (_x == _x -> _M_parent -> _M_right) { _x = _x -> _M_parent; _Rb_tree_rotate_left(_x, _root); } // 将插入节点的父亲节点改为黑色 _x -> _M_parent -> _M_color = _S_rb_tree_black; // 祖父节点为红色 _x -> _M_parent -> _M_parent -> _M_color = _S_rb_tree_red; // 将祖父节点做右旋转操作 _Rb_tree_rotate_right(_x -> _M_parent -> _M_parent, _root); } } else { // 父亲节点为祖父节点的右孩子节点 // 令y为叔叔节点,作为祖父节点的左孩子节点 _Rb_tree_node_base* _y = _x -> _M_parent -> _M_parent -> _M_left; // 叔叔节点存在,且为红色 if (_y && _y -> _M_color == _S_rb_tree_red) { // 将父亲节点改为黑色 _x -> _M_parent -> _M_color = _S_rb_tree_black; // 叔叔节点改为黑色 _y -> _M_color = _S_rb_tree_black; // 祖父节点为红色 _x -> _M_parent -> _M_parent -> _M_color = _S_rb_tree_red; // 将插入节点指向祖父节点 _x = _x -> _M_parent -> _M_parent; } else { // 无叔叔节点 // 插入节点是父亲节点的左孩子节点 if (_x == _x -> _M_parent -> _M_left) { // 指向插入节点的父亲节点 _x = _x -> _M_parent; // 右旋转操作 _Rb_tree_rotate_right(_x, _root); } // 将插入节点的父亲节点改为黑色 _x -> _M_parent -> _M_color = _S_rb_tree_black; // 祖父节点为红色 _x -> _M_parent -> _M_parent -> _M_color = _S_rb_tree_red; // 将祖父节点做左旋转操作 _Rb_tree_rotate_left(_x -> _M_parent -> _M_parent, _root); } } } // 根节点永远为黑 _root -> _M_color = _S_rb_tree_black; }
左旋
inline void _rb_tree_rotate_left(_Rb_tree_node_base* x, _Rb_tree_node_base*& root) { // x为旋转点 _Rb_tree_node_base* y = x -> right; // 令y为旋转点的右子节点 x -> right = y -> left; if (y -> left != nullptr) { y -> left -> parent = x; // 设定y的父节点 } if (x == root) root = y; else if (x == x -> parent -> left) x -> parent -> left = y; else x -> parent -> right = y; y -> left = x; x -> parent = y; }
右旋
inline void _rb_tree_rotate_right(_Rb_tree_node_base* x, _Rb_tree_node_base*& root) { _Rb_tree_node_base* y = x -> left; x -> left = y -> right; if (y -> right != nullptr) { y -> right -> parent = x; } if (x == root) root = y; else if (x == x -> parent -> left) x -> parent -> left = y; else x -> parent -> right = y; y -> right = x; x -> parent = y; }
三. 删除(这里的子节点均指非NIL节点)
1. 无子节点时,删除节点可能是红色后者黑色
1.1 如果为红色,直接删除即可,不会影响黑色节点的数量
1.2 如果为黑色,则需要进行删除平衡操作
2.只有一个子节点时,删除节点只能是黑色,其子节点为红色,否则无法满足红黑树的性质。此时用删除节点的子节点接到父节点,且将子节点颜色涂黑,保证黑色数量
3.有两个子节点时,与二叉搜索树一样,使用后继节点作为替换的删除节点(可以选左儿子中的最大元素或者右儿子中的最小元素放到待删除节点的位置,就可以保证结构不变。当然要记得调整子树,毕竟又出现了节点删除。习惯上选择左儿子中的最大元素和选择右儿子最小元素一样,没有差别,只是人们习惯从左到右,通常选择左儿子的最大元素放到待删位置), 情形转至为1或2处理
参考链接:https://algs4.cs.princeton.edu/33balanced/

浙公网安备 33010602011771号