码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2016-05-12 23:45:20      阅读:375      评论:0      收藏:0      [点我收藏+]

标签:

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)
{
    
}

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

标签:

原文地址:http://blog.csdn.net/derkampf/article/details/51340128

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!