红黑树的节点增加一个成员变量,表示节点的颜色:红色或是黑色.
通过对任何一条从根到分支尾部路径上各个节点颜色进行约束,红黑树确保没有一条路径会比其他路径长出两倍.近似的平衡
因此是近似平衡的.
1 红黑树特点
-
一定满足:每个节点或是红色或是黑色
-
一定满足:叶子节点(nullptr)是黑色
-
也就是子节点为
nullptr
,nullptr
是黑色根节点是黑色
-
如果一个节点是红色,那么他的两个子节点都是黑色
父子之间可以同时是黑色,但不能同时是红色
-
每个节点,从该节点到所有后代叶节点的简单路径上,均包含相同数目的黑色节点
因为,父子节点可以同为黑色,并且黑高相同,因此,差距最多为两倍:一条红黑相间,一条全为黑。
从任意节点出发,任意一条简单路径上的黑色节点个数成为黑高,bh.而红黑树的黑高为根节点的黑高.
n个节点的红黑树,黑高最大为2lg(n+1).
刚插入的节点染色为红色,因此需要调整
2 实现
2.1 节点结构
struct RBNode { RBNode* left; RBNode* right; RBNode* p; char color; //取值为 r b int key; string value; RBTree(const RBTree& node) :key(node.key),value(node.value),left(nullptr),right(nullptr),p(nullptr), color(‘r‘)//初始化的时候为黑色 {} };
新插入的节点默认染为红色.
因此插入新节点以后,每个节点后依然会是红或黑,黑高不变(插入的染红了),
可能破坏的就是2和4:根节点是黑色(当之前为空的时候),
如果违反了性质2,表明,红黑树之前是一颗空的树,插入的节点变为根节点,此时根节点为红色.违反了.还有会面的迭代处理时候会违反。
违反性质4,表明,新插入节点的父节点也是红色.、
下面的代码还是找插入点的。
RBTree* RBTree::insert(const RBTree& node) { RBTree* cur = mp_root; RBTree* pre = nullptr; while(cur != nullptr) { if(cur->key > node.key) { pre = cur; cur = cur->left; }else if(cur->key < node.key){ pre = cur; cur = cur->left; }else if(cur->key == node.key) { return nullptr; } } RBTree* node = new RBTree(node);//color = ‘r‘ if(pre == nullptr) { m_root = node; }else{ if(pre->key>node.key) { pre->left =node; }else{ pre->right = node; } } aux_insert_rebalance(node); }
-
如果
node
节点没有父节点,那么表示,node是一个根节点,直接把node
染黑就行了.满足了根节点是黑色.
-
如果
node
有父节点,其父节点颜色是黑色,那么满足一切条件 -
如果
node
父节点是红色.那么表明,node->p->p != nullptr
,(node 的祖父节点需要是黑色)。同时也需要处理.根据如果
node->p
是左右节点来分别判断-
如果
node->p == node->p->p->left
,同时存在node->p->p->right
,而且node->p->p->right ==‘r‘
意思是,父节点和叔节点都是红色,祖父节点是黑色,自己是红色
此时,将祖父节点染红,然后叔父节点染黑.
保持了黑高不变,但是讲违反规则的节点变为祖父节点
因为,祖父节点的父节点可能是红色.
node=node->p->p
去处理祖父节点的违规.变为情况2,或3
-
如果
node->p == node->p->p->right
,同时存在node->p->p->right
,而且node->p->p->right ==‘b‘
当自己和父节点的都是红色,而自己是一个右节点的时候,叔节点是黑色,那么左旋自己,让自己变为左节点.
变为情况3
-
如果
node->p == node->p->p->left
,同时存在node->p->p->right
,而且node->p->p->right ==‘b‘
当自己和父节点的都是红色,而自己是一个左节点的时候,叔节点是黑色
直接右旋自己的祖父节点,,让自己和祖父节点处在同一级上,都是一红一黑.
将父节点染黑,之前的祖父节点染红.
处理完毕.
-
如果没有叔节点,那么默认叔节点是黑色的.
-
void RBTree::aux_insert_rebalance(RBTree *node) { while(node->p !=nullptr && node->p->color == ‘r‘) { if(node->p=node->p->p->left) { uncle = node->p->p->right; if(uncle!=nullptr && uncle->color == ‘r‘) { //如果 node 的叔节点也是红色,那么表明node 的父节点也是红色. //因为黑高相等,叔叔是红色,因此祖父是黑色,因此父节点也是红色. //此时,修改叔节点和父节点为黑色,修改父节点为红色 //因此保持红色节点不相连,黑高不变. //让 node 变为 node 的祖父, //去处理,祖父节点变为红色导致的性质破坏. node->p->color = ‘b‘; uncle->color =‘b‘; node->p->p->color = ‘r‘; node = node->p->p; } if(node==nodep->right && (uncle!=nullptr || uncle->color == ‘b‘)) { //能到这里,表明 uncle 存在而且为黑色,或是uncle不存在 //不存在的节点,默认为黑色. //并且 node 是一个右节点 //旋转,让他变为左节点 node=node->p; aux_rotate_l(node); } if(node==node->p->left && (uncle!=nullptr || uncle->color == ‘b‘)) { node->p->color = ‘b‘; node->p->p->color = ‘r‘; aux_rotate_r(node); } }else{ //上面的 right 和 left 互换 } } mp_root->color=‘b‘; }
整个平衡操作在于将不平衡状态变换到:自己和父节点是红色(一定是这种情况,而且祖父节点是黑色.),自己和父节点相对于祖父节点在同一方向,比如左左,然后,自己的叔节点是黑色(或空),而且,此时违反的规则一定是只有一个:红色不能相邻.
然后,旋转祖父节点,让自己的父节成为祖父节点,自己和祖父节点成为兄弟节点.此时,黑高变了,自己这一侧因为少了原先祖父节点(此时相对根节点是自己的父节点,是一个红色的.)而另一侧,没变(原先的祖父节点现在的父节点是自己的父节点,一个红色的),因此此时的变化,只需要讲自己的父节点染黑,然后,自己和原先的祖父节点染红.就不在违反黑高不变.
在平衡的过程中,利用的就是,路径上增加红色节点不会增加黑高,但是一直在打破红色不能相邻的规则,而,上面的规则恰好可以修复红色不相邻的规则.