红黑树是一种二叉搜索树,每个节点增加一位来储存节点的颜色,红或黑。
红黑树通过如何一条从跟到叶子的路径上各个节点的着色方式的限制,红黑树确保没有一条路径比另一条路径长两倍。
其他性质:
根节点是黑色。
红色节点的子节点都为黑色。
叶子节点或NULL空节点都为黑色。所有原来的叶子节点都有NULL的空节点作为新的叶子节点,取代了自己的叶子节点的身份,即,所有的NULL节点都是黑色的意思。
最重要一点:任何一节点到叶子节点的NULL指针的路径经过的黑色节点数相同。
插入:
左旋 以节点p为支点的话,即p的右孩子为r,将p的右孩子赋值为r的左孩子,然后将r的左孩子赋值为p,最后将 p = r。
右旋 相反, p->left = r -> right , r->right = p 。最后 p = r 。
这里使用递归的方法插入的话,p 每次都为其父节点的 孩子指针。但查了一下,一般实现红黑树都是直接使用父指针指向,即在节点中添加一个父指针。这样以空间换空间,递归费时费空间。
插入的情况:
1.插入节点即为根节点。将其标为黑色,结束。
2.如果父节点为黑色,则结束。
如果父节点为红色:父节点为红色必定有祖父,因为根节点是黑色。。。
3.叔叔节点为红色 : 将叔叔节点和父节点改为黑色,祖父改为红色,然后以祖父节点继续判断。
4.当叔叔节点是黑色时, 若当前节点p与其父节点f与其祖父节点g不在一条直线上时,即
当f为g的左孩子时,p为f的右孩子,这时要 以f为支点左旋,
当发为g的右孩子时,p为f的左孩子,这是要以f为支点右旋。
然后以f节点做为当前节点继续判断。
5.当叔叔节点是黑色时, 若当前节点与f和g和叔叔节点在一条直线时,则以祖父节点为做为支点旋转。
当f为g的左孩子,且p为f的左孩子,则以g为支点右旋转。
当f为g的右孩子,且p为f的右孩子,则以g为支点左旋。
然后将g节点设为红色,f节点设为黑色。结束。
这种情况就不会有进一步的可能了,因为除了当前节点,其他都是正确的。父节点是红色,则父节点的右孩子必是黑色的,将右孩子连接给祖父节点后,祖父节点是红色的,不会有异常,而父节点替代了祖父节点的位置,但父节点为黑色,所以也不会有异常。
这里右旋函数要考虑祖父节点就为根节点的情况,无法再寻找到其父节点。
这里可以发现,红黑树通过红黑两种颜色的设置,方便了树的旋转,使树处于平衡,至于这样做为何能确保平衡,我就无法深入研究了。
这里查了一些资料,被某篇博客坑了一下,然后自己也试着画些过程图帮助自己理解,画比较难看:
测试时,画出了从1插入到8的情况,验证代码正确。
试着写出代码
//改变其向下的指针的同时要更新其父指针。 template <typename T> void RBTree<T>::LRotation(RBNode<T>* p){ RBNode<T> *r = p->right; if(p->parent){ if(p->parent->right == p) p->parent->right = r; else p->parent->left = r; r->parent = p->parent; }else{ root = r; r->parent = 0; } p->parent = r; if(r->left)r->left->parent = p; p->right = r->left; r->left = p; return ; } template <typename T> void RBTree<T>::RRotation(RBNode<T>* p){ RBNode<T> *r = p->left; if(p->parent){ if(p->parent->right == p) p->parent->right = r; else p->parent->left = r; r->parent = p->parent; }else{ root = r; r->parent =0; } p->parent = r; if(r->right)r->right->parent = p; p->left = r->right; r->right = p; return ; } template <typename T> bool RBTree<T>::Insert(const T &x){ if(!root){ root = new RBNode<T>(x,0); root->red = false; return true; } RBNode<T>* p = root, *fa=root; while(p){ fa = p; if(x <p->value) p = p->left; else if(x > p->value) p = p->right; else return false; } if(x < fa->value) p = fa->left = new RBNode<T>(x,fa); else p = fa-> right = new RBNode<T>(x,fa); AdjustColor(p); return true; } template <typename T> bool RBTree<T>::AdjustColor(RBNode<T> *p){ if( p == root){ p->red = false; return true; } else if( ! p->parent->red ) return true; else { RBNode<T>* uncle, *f, *g; f = p -> parent; g = f->parent; bool uncleColor;//由于叔叔节点可能为空 if(f == g->left) uncle = g->right; else uncle = g->left; if(uncle)uncleColor = uncle->red; else uncleColor = false; if(uncleColor){ uncle->red = false; f->red = false; g->red = true; //将祖父节点设为红色 AdjustColor(g); }else{ if(g->left == f){ if(f->left == p){ g->red = true; f->red = false; RRotation(g); }else{ LRotation(f); AdjustColor(f); } }else{ if(f->right == p){ g->red = true; f->red = false; LRotation(g); }else{ RRotation(f); AdjustColor(f); } } } } return true; }
这里有一点需要注意的是节点有父指针,旋转时要进行更新。