马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
前言
前面我们先容了AVL树来办理搜索二叉树不平衡的问题,本日我们带来一种新的办理方案,同时,这种办理方案在实践中比AVL树更常用,这种数据结构就是红黑树
红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。
通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是靠近平衡的。
概念还是很抽象,我们直接看图来理解红黑树
一览无余,红黑树嘛,每个节点要么是黑色,要么是赤色。
留意:红黑树并不像AVL树一样是绝对平衡的,红黑树是相对平衡的,红黑树通过让最长路径长度不超过最短路径长度的两倍来控制相对平衡。
红黑树的性子
- 每个结点不是赤色就是黑色
- 根节点是黑色的
- 假如一个节点是赤色的,则它的两个孩子结点是黑色的 (不能出现一连赤色节点)
- 对于每个结点,从该结点到其全部子女叶结点的简单路径上,均 包含相同数目的黑色结点(每条路径上黑色节点个数相同)
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
我们来思考一下,为什么这几条性子就可以帮红黑树处于平衡状态呢?
在这些性子的限制下,我们可以发现:
最短路径:全部是黑色节点
最长路径:一黑一红交替出现
以是,最长路径不会超过最短路径的两倍,以是红黑树可以到达相对平衡
红黑树的节点(以K_V型为例)
红黑树的节点,我们依旧接纳三叉链
但是,我们不需要平衡因子,
我们需要引入颜色变量,
由于只有赤色和黑色,我们就直接使用枚举范例来表示颜色
- enum Color
- {
- RED,
- BLACK
- };
- template<class K,class V>
- struct RBTreeNode
- {
- RBTreeNode<K,V>* _left = nullptr;
- RBTreeNode<K,V>* _right = nullptr;
- RBTreeNode<K,V>* _parent = nullptr;
- Color _col = RED;
- pair<K, V> _value;
-
- RBTreeNode(const pair<K,V>& value)
- :_value(value)
- {}
- };
复制代码 红黑树的插入
在学习完了AVL树之后,再来理解红黑树的插入是非常简单的。
新插入节点颜色
在学习插入之前,我们来思考一个问题,新插入的节点,我们究竟给什么颜色呢?
给黑色还是赤色呢?
假如我们给黑色,那么我们就破坏了第四条性子
此时,新插入的节点的这条路径上的黑色节点数量多了一个,直接影响了整棵树,破坏性非常强
假如我们给赤色,那么我们就破坏了第三条性子
此时,只是影响了当前的这一棵子树,破坏性比上面的情况小
于是,我们选择坏处小的这种情况。
我们给新插入节点的颜色默以为赤色。(当然根节点除外)
插入
首先还是按照搜索二叉树的插入方法一样,大的往右走,小的往左走,然后连接上父亲节点。
处理完这一系列操作之后
我们就要像AVL树更新平衡因子一样,更新颜色了。
我们插入的是赤色节点,破坏了第三条性子,不能出现一连赤色节点,以是我们需要维护颜色。
当我们需要调解的时候,一定是这种情况
cur = 红,parent = 红,grandparent = 黑
假如父亲不是红,就不需要调解,假如爷爷是红,那么早就该调解了。
因此我们只需要思量爷爷的另一个节点的颜色即可,也就是叔叔节点的颜色
叔叔存在且为赤色(变色)
此时我们只需要调解颜色即可,我们将parent 和uncle都酿成黑色,把爷爷酿成赤色,
这样既办理了一连赤色节点的问题,又没有给该路径增加新节点
调解后酿成了下图
此时爷爷变红了,需要担心爷爷的父亲是否也是赤色,那么就需要向上继承调解,直到爷爷的父亲不是赤色节点为止。
留意:根节点除外,当爷爷的父亲不存在的时候,就到了根节点,根节点始终要保持黑色,我们可以在出循环之后,给根节点置为黑色,就可以避免在循环内额外处理这种情况。
叔叔不存在或叔叔为黑色(旋转 + 变色)
(1)单旋
此时单纯的变色已经不可以大概办理问题了,需要额外引入旋转
看左边这个图,出现了一连的赤色节点,需要调解了,分析左边高了,我们就需要通过旋转来控制平衡,
这里的旋转和AVL树的旋转操作一样,
这里进行右单旋即可
右单旋之后,酿成了右图
旋转之后,将父亲置为黑色,爷爷置为赤色
即消除了一连赤色节点,又没有新增每条路径上的黑色节点。
左单旋也是一样,就不多赘述了。
(2)双旋
双旋和单旋的区别在于,cur究竟是在parent的左边还是右边
假如在左边就是完全失衡,只需要右单旋,假如在右边则偏了,需要先左单旋成完全失衡,再右单旋进行调解。
像第一幅图,cur在parent的右边,就需要进行双旋,先一parent为root,进行左单旋,
就酿成了第二幅图,此时以grandparent为root进行右单旋即可
就酿成了第三幅图
此时对颜色的调解为
cur需要酿成黑色,grandparent需要酿成赤色(图中出现了一点问题,信任读者能看出来)
对于旋转,我们在旋转完成之后的子树的根节点就是黑色,不大概出现一连的赤色节点,以是不需要继承向上调解,我们也就可以跳出循环,直接break掉。
另外另一个方向的双旋也是类似,这里也不多赘述了
代码
- #include <iostream>
- using namespace std;
- namespace bit
- {
- enum Color
- {
- RED,
- BLACK
- };
- template<class K,class V>
- struct RBTreeNode
- {
- RBTreeNode<K,V>* _left = nullptr;
- RBTreeNode<K,V>* _right = nullptr;
- RBTreeNode<K,V>* _parent = nullptr;
- Color _col = RED;
- pair<K, V> _value;
- RBTreeNode(const pair<K,V>& value)
- :_value(value)
- {}
- };
- template<class K,class V>
- class RBTree
- {
- typedef RBTreeNode<K, V> Node;
- private:
- Node* _root = nullptr;
- void RotateR(Node* parent)
- {
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
- Node* pparent = parent->_parent;
- parent->_left = subLR;
- if (subLR)
- subLR->_parent = parent;
- subL->_right = parent;
- parent->_parent = subL;
- if (pparent == nullptr)
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- else
- {
- subL->_parent = pparent;
- if (pparent->_left == parent)
- {
- pparent->_left = subL;
- }
- else
- {
- pparent->_right = subL;
- }
- }
- }
- void RotateL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
- Node* pparent = parent->_parent;
- parent->_right = subRL;
- if (subRL)
- subRL->_parent = parent;
- subR->_left = parent;
- parent->_parent = subR;
- if (pparent == nullptr)
- {
- _root = subR;
- subR->_parent = nullptr;
- }
- else
- {
- subR->_parent = pparent;
- if (pparent->_left == parent)
- {
- pparent->_left = subR;
- }
- else
- {
- pparent->_right = subR;
- }
- }
- }
- void inorder(Node* root)
- {
- if (root == nullptr)
- return;
- inorder(root->_left);
- cout << root->_value.first << ":" << root->_value.second << endl;
- inorder(root->_right);
- }
- public:
- void InOrder()
- {
- inorder(_root);
- }
- bool insert(const pair<K, V>& value)
- {
- if (_root == nullptr)//空树就直接申请节点,将根节点处理成黑色
- {
- _root = new Node(value);
- _root->_col = BLACK;
- return true;
- }
- Node* cur = _root;
- Node* parent = nullptr;
- while (cur)
- {
- if (value.first > cur->_value.first)
- {
- parent = cur;
- cur = cur->_right;
- }
- else if (value.first < cur->_value.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- return false;//已经存在就插入失败
- }
- }
- cur = new Node(value);
- cur->_parent = parent;
- if (value.first > parent->_value.first)
- {
- parent->_right = cur;
- }
- else
- {
- parent->_left = cur;
- }
- while (parent && parent->_col == RED)
- {
- Node* grandparent = parent->_parent;
- if (parent == grandparent->_left)//parent在左侧
- {
- Node* uncle = grandparent->_right;
- if (uncle && uncle->_col == RED)//叔叔存在且为红
- {
- parent->_col = uncle->_col = BLACK;
- grandparent->_col = RED;
- cur = grandparent;
- parent = cur->_parent;
- }
- else//叔叔不存在或者叔叔为黑色,则进行旋转 + 变色
- {
- if (cur == parent->_left)//进行右单旋即可
- {
- RotateR(grandparent);
- parent->_col = BLACK;
- grandparent->_col = RED;
- }
- else
- {
- RotateL(parent);
- RotateR(grandparent);
- cur->_col = BLACK;
- grandparent->_col = RED;
- }
- break;
- }
- }
- else//parent在右侧
- {
- Node* uncle = grandparent->_left;
- if (uncle && uncle->_col == RED)//叔叔存在且为红
- {
- parent->_col = uncle->_col = BLACK;
- grandparent->_col = RED;
- cur = grandparent;
- parent = cur->_parent;
- }
- else//叔叔不存在或者叔叔为黑色
- {
- if (cur == parent->_right)
- {
- RotateL(grandparent);
- parent->_col = BLACK;
- grandparent->_col = RED;
- }
- else
- {
- RotateR(parent);
- RotateL(grandparent);
- cur->_col = BLACK;
- grandparent->_col = RED;
- }
- break;
- }
- }
- }
- _root->_col = BLACK;
- return true;
- }
- };
- }
复制代码 关于红黑树的删除,这里就不进行解说了,感兴趣的同砚可以去看看算法导论和殷人昆老师的数据结构课本,内里可以找到答案。
|