自己扯扯红黑树

来源:互联网 发布:java 轻量级orm框架 编辑:程序博客网 时间:2024/06/05 06:22
因为最近挺多人问我面试的红黑树的东西,所以我就写一篇文章来总结下红黑树吧.以下仅代表个人观点,如果有写错的地方请指正哈.

首先,要知道为什么需要红黑树.在算法导论对R-B Tree的介绍:红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。所以它是一颗近似平衡的二叉搜索树,增删改查效率高.
之后再想想,它是如何保证近似平衡的.所以就要知道红黑树的以下几个特点:

    1.每个结点要么是红的要么是黑的。      2.根结点是黑的。      3.每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。      4.如果一个结点是红的,那么它的两个儿子都是黑的。      5.对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。

好了,一般点的面试问完几个特点和使用范围应该就不会怎么问了,但作为一个好奇心很强的小孩,肯定想去深入了解下它如何在增删的时候保证以下的几个特点的完整,也就是它是如何维护这颗二叉树使得它一直是红黑树的.
首先要明确一点,就是它在插入和删除的各种的调整就是为了维护红黑树的5个特点的,切记切记.
最容易的就是插入的过程.因为特点中说从根到叶节点的黑节点数要相同.所以每次插入的要是是红色的节点那就肯定不会影响红黑树的这几个特点了,所以我们就每次插入红色的节点.但又有个问题,红节点的孩子不能是红色的,所以要进行变色调整.接下来就详细描述一下这个过程好了.
1.最简单的情况插入的位置的父节点是黑色的,那就直接插就好了,不用做调整.因为它的插入不会影响红黑树的性质.
2.插入的是根节点,那直接把这个节点变成黑色就好了,因为根节点必须是黑色的.
3.插入的节点的父节点是红色的,这个是最麻烦的.也是最复杂的插入.
3.1父节点是红色的且叔叔节点(叔叔节点就是父亲的兄弟节点)是红色的.
因为父亲是红色的,所以必须有爷爷节点(父亲的父节点).且爷爷是黑色的,那么解决策略就是:将当前节点的父节点和叔叔节点涂黑,祖父结点涂红,把当前结点指向祖父节点,从新的当前节点重新开始算法.

//伪代码如下while z.p.color == RED     do if z.p == z.p.p.left         then yz.p.p.right         if y.color == RED  

下面引用2个维基百科的图片.方便理解,同时方便我扯下面的解释(N是当前插入的,P是父,U是叔,G是祖父)
调整前:
调整前
调整后:
这里写图片描述
好了,很多博客描述到这就讲下一个情况了,这里我废话点说详细点好了,为什么这么做呢.原因:它的插入使得红黑树的性质被破坏了,所以,我们就先维护爷爷节点这一块往下的子树,使得其满足红黑树的性质,然后再往上接着维护.那这么维护真的可以么,嗯,因为如果将父亲和叔叔都变黑了,祖父变成红色,那么这一整颗子树的从祖父到叶子的黑节点数还是和插入这货之前的一样,所以这方法是可以的.
3.2父节点是红色,叔叔节点是黑色.这个可以用一个图表来总结.(本人写字丑,忍忍吧).
这里写图片描述
引用JULY的图片和代码:

PS:第二个图看红色节点的旋转的意思就好了.

(引用原文链接:http://blog.csdn.net/v_JULY_v/article/details/6124989)

void insert_case4(node n) {     if (n == n->parent->right && n->parent == grandparent(n)->left) {         rotate_left(n->parent);         n = n->left;     } else if (n == n->parent->left && n->parent == grandparent(n)->right) {         rotate_right(n->parent);         n = n->right;     }     insert_case5(n);    //转到下述情形5处理。

这里写图片描述

void insert_case5(node n) {     n->parent->color = BLACK;     grandparent(n)->color = RED;     if (n == n->parent->left && n->parent == grandparent(n)->left) {         rotate_right(grandparent(n));     } else {         /* 反情况,N 是其父节点的右孩子,而父节点P又是其父G的右孩子 */         rotate_left(grandparent(n));     } }

这里写图片描述
好了,这里解释下case4和5.
case4和case5是配套的,因为不可能只出现case4,出现case4都会转到case5,简单的说,case4就是将树调整成case5的适用条件.然后case5的调整原理就是,(举个例子,用图中左左的的情况)因为插入是红的,父是红的,且插入的和父亲都是左子,叔叔是黑的,那祖父肯定是黑的,所以变色后右旋一次,路径中的黑子数还是一样的,也没有红节点孩子是红色,保证了红黑树的性质,所以这个解决方案就是可行的了.
到此插入就讲完了,很简单吧.
接下来就是删除部分,删除有点麻烦,过更新.

0 0
原创粉丝点击