如果你已经掌握了红黑树前篇的内容,那么很高兴你已经具备了学习红黑树的资本。左旋转和右旋转是红黑树中为了调节红黑个数而设计的方案,所以,此篇主要讲解一下旋转问题。旋转是红黑树中最简单的东东,不过也是最基础的,由于涉及到红黑树的性质,所以先从学习红黑树的五个性质开始吧。
1,红黑树的五个性质:
1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。
2,红黑树的旋转:
当我们在对红黑树进行插入、删除等操作时,对树做了修改,那么可能会违背红黑树的性质。
为了保持红黑树的性质,我们可以通过对树进行旋转,即修改树中某些结点的指针结构,同时需要更改某些节点的颜色,以达到对红黑树进行插入、删除结点等操作时,红黑树依然能保持它特有的性质。
旋转如下图:
其中:旋转过程中二叉搜索树(BST)性质不变:α≤x≤β≤y≤γ
我们以左旋为例进行解析,左旋如下图:
图中红线标注的步骤如下(right[x]:x的右节点;p[x]:x的父节点):
①y←right[x]
②right[x]←left[y], p[left[y]]←x
③p[y]←p[x], p[x]的左或右指针指向y (p[x]为根节点则root←y)
④ Left[y]←x, p[x]←y
经过此四个步骤即可完成左旋操作。
3,代码实现:
//root:传入的树的根节点,x:旋转的基点。
Node* left_rotate( Node* root, Node* x )
{
Node* y;
//左旋结点必须有右子结点
if( x->right == &tree_nil )
{
printf( "have no right child,rotation cancel.\n" );
return root;
}
//Step1:记录y的位置,其为x的右节点 == y←right[x]
y = x->right;
//Step2:y的原来的左节点变为x的右节点 == right[x]←left[y]
x->right = y->left;
//y的原来的左节点的父节点变为x == p[left[y]]←x
if( y->left != &tree_nil )
y->left->parent = x;
//Step3:原来的x的父结点变为现在的y的父结点 == p[y]←p[x]
y->parent = x->parent;
if( x->parent == &tree_nil )
root = y;
else if( x == x->parent->left )
x->parent->left = y;
else
x->parent->right = y;
//Step4:x为y的左节点,y为x的父节点。
y->left = x;
x->parent = y;
return root;
}
4,左旋+节点颜色修改:
为什么要旋转,最终的目的还是为了实现红黑树的五个性质,以下图为例解释说明左旋操作:
此图操作步骤为(主要对性质5进行说明,其余性质一目了然):祖、父两个节点互换颜色,最后对祖节点进行左旋转。颜色互换后旋转,是的为红色的祖节点成了左子树上的一点,左子树添加红节点不影响其黑节点个数。同时,对于右子树来说,父变成了根节点,但是父变为了黑色,其黑节点个数依然不变。其中1由右边改为了左边,但是他的父节点的颜色是一直的,且其现在的父节点和原来父节点是兄弟关系,古其黑节点个数也不变。
5,右旋和左旋类似,不做叙述。