您的位置:首页 > 编程语言 > C语言/C++

C++ Learning——Build a simple&little RB_Tree

2016-05-08 21:39 381 查看
Why would we go to such ludicrous lebgths to explain the RB_TREE?

《STL源码剖析》上给了我们一个很好的解释:(见202页)

所谓树形平衡与否,并没有一个绝对的测量标准。“平衡”的大致意义是:没有一个节点过深(深度即就是“根节点至任一节点的路径长度,即所谓该节点的深度”,在数值上与路径长度相等)。不同的平衡条件,造就出不同的效率表现,以及不同的实现复杂度。有数种特殊结构如:AVL-Tree,RB-tree,AA-tree,均可实现出平衡二叉搜索树,他们都比一般的(无法绝对维持平衡的)二叉搜索树更加复杂,因此,插入节点和删除节点的平均时间也比较长,但是他们可以避免极难应付的(高度不平衡)最坏情况,而且由于他们总是保持某种程度的平衡,所以元素的访问(搜寻)时间平均而言也就比较少,一般而言其搜寻时间可以节省25%左右

一个平衡二叉搜索树之RB_Tree.它不仅是一个二叉搜索树,还应该满足以下规则:

概括起来即是“一头一脚黑,黑同红不连”:

我们约定四个规则:

(1)每个节点不是红色就是黑色

(2)根节点必须为黑色(即一头一脚黑,其中一脚指的是空节点)

(3)如果节点为红,其子节点必须为黑(即红不连)

(4)任一节点到NULL(树尾端)的任何路径,所含之黑节点数必须相同。 (即黑同)

Here are the RB_TREE code && explanation:(You also can actualize it.Believe yourself!)

/*头文件*/
#include<iostream>
#include<assert.h>   //for assert
#include<string.h>   //for memset

/*memset用法:
* void *memset(void *s, int c, size_t n);
*  The  memset() function fills the first n bytes of the memory area
* pointed to by s with the constant byte c.
*/

using namespace std;

/*树节点颜色用枚举类型来定义
*typedef的使用方法见我的另一篇文章[typedef用法汇总](http://blog.csdn.net/derkampf/article/details/51339953)
*这里用来定义节点中所存实值的类型。
*/
typedef int Type;
typdef enum{RED=0, BLACK}Color;

typedef struct Node
{
Color color;
Type key;
struct Node *left, *right, *parent;
}*PNode;    //节点的类型为指针类型

typedef struct
{
Node *root;
Node *Nil;
}RB_TREE;    //RB_TREE节点内容,Nil为一个工具,为后续树之节点的的赋空、判空起作用

Node* Buynode()
{
Node *p = new Node;
assert(p != NULL);
memset(p,0,sizeof(Node));    //为新增节点节点初始化,{color = RED, key = 0, left = 0x0, right = 0x0, parent = 0x0}
return p;
}

void InitTree(RB_TREE &t)    //创建节点并对其初始化
{
t.Nil = Buynode();
t.root = t.Nil;
t.Nil->color = BLACK;
t.Nil->key = -1;
}

/*左旋、右旋,以及插入之后将红黑树平衡的代码置于后面进行分析*/

bool Insert(RB_TREE &t, Type x)    //插入节点
{
Node *p = t.Nil;
Node *s = t.root;
/*从根结点开始遍历,找到属于新增节点q的位置,将其插入*/
while(s != t.Nil)
{
p = s;
if(x == s->key)
{
return false;
}
else if(x < s->key)
{
s = s->left;    //说明x的位置应该在此时s的左树位置查找
}
else
{
s = s->right;    //那么此时x应插入的位置应该继续在s的右树上寻找
}
}
/*到达此步,程序已经找到新增节点的位置,接下来要做的是:新建一个空节点q,将x的值给与该结点(这就构成我们要插入的新节点)*/
Node *q = Buynode();
q->key = x;
q->parent = p;   //仔细想想,这里不能写q->parent = s;因为程序执行到这里的时候s = t.Nil,即s为NULL
/*后续操作*/
if(p == t.Nil)    //这里我感觉可以没有这个条件,因为此时p是存在的
{
t.root = q;
}
else if(x < p->key)
{
p->left = q;
}
else
{
p->right = q;
}
q->left = q->right = t.Nil;
q->color = RED;    //新插入节点颜色须为红色
Insert_Fixup(t,q); //需要进一步修正一下,是的满足RB_TREE的规则
return true;
}

int main()
{
int ar[] = {5,7,10};
RB_TREE rb;
InitTree(rb);
for(int i = 0; i<sizeof(ar) / sizeof(int); ++i)
{
Insert(rb, ar[i]);
}
return 0;
}


插入的节点会破坏RB_Tree的规则,致使我们必须旋转树形并调整节点的颜色。书中给了我们四种考虑办法。

“根据x的插入位置即外围节点(s伯父节点和GG曾祖父节点)的颜色,有了四种考虑”

——《STL源码剖析》

因为是简单实现,我们只来剖析一下单旋转的情况。后面我将陆续为大家更新SGI–STL版红黑树的实现过程,敬请期待!

下面即为左旋、右旋,以及插入之后将红黑树平衡之的代码:

情况如下:



(详情见《STL源码剖析》210页,这里不再赘述)



//有了上面的树,得到下面的代码是显而易见的
void Rotateleft(RB_TREE &t, Node *p)
{
Node *s = p->right;
P->right = s->left;
if(s->left != t.Nil)
{
s->left->parent = p;
}
s->parent = p->parent;
if(p->parent = t.Nil)
{
t.root = s;
}
else if(p = p->parent->left)
{
p->parent->left = s;
}
else
{
p->parent->right = s;
}
s->left = p;
p->parent = s;
}




//上面是右旋的示意图,只要考虑到所会出现的各种情况,那么就可以写出通用的右旋代码。
void Rotateright(RB_TREE &t, Node *p)
{
Node *s = p->left;
p->left = s->right;
if(s->right != t.Nil)
{
s->right->parent = p;
}
s->parent = p->parent;
if(p->parent == t>Nil)
{
t.root = s;
}
else if(p == p->parent->left)
{
p->parent->left = s;
}
else
{
p->parent->right = s;
}
s->right = p;
p->parent = s;
}


我们要知道的是,一段代码用来解决一个问题,如果考虑到各种可能会出现的情况,那么这段代码便可以完美的解决这个问题中出现的所有情况。就像上述的左旋,右旋代码,我们只用它来解决左旋,或者右旋的问题,在编写代码的时候,我们已经考虑到了在这一过程中会出现的情况,那么它不仅仅只局限于简单的树,而是所有需要进行左旋或者右旋的树。

void Insert_Fixup(RB_TREE &t, Node *z)
{

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: