计算机中的红与黑(二)
三、左旋转和右旋转
在二叉搜索树当中有时需要对树进行旋转,来修复被破坏的某种性质,并且保持二叉搜索树本身的性质不变,这里主要包括了左旋转和右旋转
左旋转:以根节点和右子节点之间的连线为轴进行逆时针旋转。假设原来的根节点为node,右子节点为right,左子结点为left,旋转的结果是右子节点成为了新的根节点,并且右子节点的左孩子成为了原来的根节点的右孩子。
在这样的情况下,二叉搜索树的性质得到了保持。
右旋转:操作过程与左旋转正好相反,以根节点和左子结点之间的连线为轴进行顺时针旋转。假设原来的根节点为node,右子节点为right,左子结点为left,旋转的结果是左子结点成为了新的根节点,并且左子结点的右孩子成为了原来的根节点的右孩子。
四、红黑树
由于红黑树是一种二叉搜索树,对于他的操作都是基于上面的对于二叉搜索树的讨论,显然两者查找操作时一样的。
对于insert和delete操作可能会造成红黑树性质的破坏,因此需要进行调整。
1、insert
为了保持红黑树性质5,被插入的节点的颜色是红色的,这是可能会破坏性质2和性质4. 假设插入的节点就是根节点,则违反根节点为黑色的规定;假设插入的节点不是根节点但是出现了其父节点也为红色情况,则破坏了性质4.
boolean rbtinsert(Node * root, Node * newnode){ boolean result=insert(root,newnode); if(result==false) return false; newnode->color=RED; rbtinsert_fixup(newnode); return true; }
为了支持这里的操作,需要知道真正被插入的节点的位置,这个节点实际上就是newnode
下面详细介绍rbtinsert_fixup操作过程
所谓授人以鱼不如授人以渔,在介绍具体的算法之前,我们先想想都会有啥情况?
对于本插入的节点正好为根节点从而破坏性质2的情况,由于过于简单,只需将被插入的节点改为黑色,问题即可破,我们将其作为一个Corner case,先将其放到一边,现在集中精力考虑两个红色的情况。既然上面介绍了,旋转,我们现在回想,我们能否通过旋转来解决这个问题呢?问题答案当然是OK,甚至有时根本不需要旋转就可以解决问题。
我们先对问题可能遇到的情况进行,汇总,由于要考虑旋转了,所以要多考虑一层节点,所以要上升到当前节点的祖父节点。祖父节点是什么颜色呢?当然是黑色的。因为现在我们知道父节点是红色,由于原来的红黑树是合法的,所以祖父节点应该为黑色。那么祖父节点的另一个孩子是什么颜色呢?红色?黑色?当然都有可能。
情况一、如果是红色,许多地方成这种情况为红叔节点,干脆叫做红薯吧!这种情况怎么办呢?我们可以把父节点和红薯节点染为黑色,在把祖父节点染为红色,当前节点上升到祖父节点,继续进行判断。这样进行颜色变换之后,被修改的路径上黑色节点的个数没有改变。和原来是一样的。
有的人会想,既然增加了一个节点,这样变换下去应该有导致黑色节点增加一个的情况。是的!当要检查的当前节点正好为根节点时,由于根节点的父节点NIL为黑色,所以算法停止,同时将更节点染为黑色(在算法的上一次迭代被染成了红色),从而导致红黑树整体的红色节点的个数增加了一个。
情况二、如果是黑色呢?这是情况变得似乎复杂了一点,但是仔细想想,我们的大招还没有上呢?我们会发现,进行适当的旋转和颜色变换之后,算法可以在这种情况下结束。我们可以先考虑一个具体的情况。假设当前节点是x,x是x的父节点f的左孩子,f是f的父节点ff的左孩子,同时ff的右孩子是y。各自的颜色上面的描述已经很清楚了,在重复一遍,x,f,是红色,ff和y是黑色。下一步怎么做?
经过上面的两步走,我们发现,现在的红黑树已经是一棵合法的红黑树了,在左子树上的连续的两个红色节点不复存在,同时每个分之上的黑色节点个数没有改变。由于进行的是右旋转,所以二叉搜索树的特性也没有改变。
情况三、我们再考虑一种特殊情况,假设x是f的右孩子怎么办?如下图所示。
经过上面的旋转,这种特殊的情况,转化为了上面的第二种情况,那么进行一次右旋转算法,就可以结束了。
上面的三种情况考虑的都是x的父节点是x的祖父节点ff的左孩子的情况,对于相反的情况和这种情况的处理方式是对称的,在这里不在赘述。
复杂度分析:
算法的第一种情况是导致,当前节点上升到祖父节点,进行递归的检查操作,并且没有旋转操作;对于另外两种情况,最多会有两次旋转操作,算法就可以在这里停止。因此最坏情况下的复杂度是O(logn)
但是我们发现他的节点旋转的次数非常的少,最多只有两次!
posted on 2015-05-29 20:00 lightblueme 阅读(204) 评论(0) 编辑 收藏 举报