红黑树(二)插入

  接下来介绍红黑树的插入操做,介绍插入以前,咱们先来了解一下红黑树的性质。node

  一、每一个节点不是红色就是黑色spa

  二、跟节点为黑色。3d

  三、若是节点为红,子节点必须为黑。指针

  四、任意节点至树尾端的任何路径,黑节点必须相同。code

  

规则4主要是保证树的平衡性,不过它的要求不是很严。主要是为了减小调整操做。根据规则4,咱们能够判断出新节点都是红节点。(若是新节点是黑节点,那么每次插入都要进行调整)blog

因为要常常使用某个节点的父亲。因此这里添加了一个指向父亲的指针。因此咱们先看一下红黑树的结构组成。class

typedef int value_type;
typedef bool color_type;
const color_type red = true;
const color_type black = false;

typedef struct node
{
    value_type value;
    color_type color;

    struct node * parent;
    struct node * left;
    struct node * right;
} Node;

typedef struct Tree
{
    Node * root;
} Tree;

Node节点,value,关键字信息。color,颜色。parent,left,right,父亲,左孩子,右孩子。二叉树

Tree只有一个根节点。循环

插入操做,与二叉树的插入是同样的。im

首先是找到插入位置。

//根节点返回NULL,找到返回当前节点,不然返回x的父亲(x是节点所在的位置)
static Node * search_node(const int key, Tree * tree)
{
    Node * x, * y;

    x = tree->root;
    y = nil;

    while ( x != nil )
    {
        y = x;
        if ( key < x->value )
            x = x->left;
        else if ( key >  x->value )
            x = x->right;
        else
            break;
    }

    return y;
}

nil是一个空节点,全部应该为空的指针都指向它。这个节点是黑色的,parent,left,right都为NULL,没有初始化value。(不是必定要有nil节点)

其次将元素插入

void rb_insert(const value_type value, Tree * tree)
{
    Node * new_node;//新节点
    Node * x = search_node(value, tree);//插入点
    
    if ( x == nil )
    {
        tree->root = make_new(value);
        tree->root->color = black;//根节点为黑色
    }
    else
    {
        if ( x->value == value )//关键字不可重复
            return;

        new_node = make_new(value);
        if ( value < x->value )//将新节点插入二叉树中
            x->left = new_node;
        else
            x->right = new_node;
        new_node->parent = x;//设置新节点的父亲

        if ( x->color == red )//若是新节点的父亲为红色,调整
            insert_fixup(new_node, tree);
    }
}

新节点必定是红色的,因此不会违反规则4,但有可能违反规则3。也就是说若是插入节点的父亲是红色的,那么违反了规则3。

咱们须要进行调整。

调整时会遇到三种状况,根据不一样的状况进行不一样的调整。

调整是一个循环过程,当x为根节点或者x是黑色时结束。(这个操做次数并不会不少)

三种状况在代码中看注释。

static void insert_fixup(Node * x, Tree * tree)
{
    Node * y;//y是x的伯父节点
    while ( x != tree->root && x->parent->color == red )
    {
        if ( x->parent == x->parent->parent->left )//父亲是祖父左孩子
        {
            y = x->parent->parent->right;

            //case 1:    伯父是红色
            //处理办法
            //        1.将父亲,伯父变成黑色
            //        2.将祖父变成红色
            //        3.将祖父设为x,向上检查
            if ( y->color == red )
            {
                x->parent->color = black;
                y->color = black;
                x->parent->parent->color = red;
                x = x->parent->parent;
            }
            else
            {
                //case 2:    x是父亲的右孩子
                //处理办法:
                //        1.将x设为x的父亲
                //        2.以x为旋转点,左旋
                //通过上述操做,转换成case 3
                if ( x == x->parent->right )
                {
                    x = x->parent;
                    left_rotate(x, tree);
                }
                
                //case 3:    x是父亲的左孩子
                //处理办法:
                //        1.将x的父亲设为黑色
                //        2.将x的祖父设为红色
                //        3.以x的祖父为旋转点,右旋
                x->parent->color = black;
                x->parent->parent->color = red;
                right_rotate(x->parent->parent, tree);
            }
        }
        else//与上述处理相同,只是将left与right互换
        {
            y = x->parent->parent->left;

            if ( y->color == red )
            {
                x->parent->color = black;
                y->color = black;
                x->parent->parent->color = red;
                x = x->parent->parent;
            }
            else
            {
                if ( x == x->parent->left )
                {
                    x = x->parent;
                    right_rotate(x, tree);
                }

                x->parent->color = black;
                x->parent->parent->color = red;
                left_rotate(x->parent->parent, tree);
            }
        }
    }//while end

    tree->root->color = black;
}

这样插入操做就完成了。

这是一棵红黑树,圆圈里的表明黑色节点,没有圆圈的表明红色。你们能够利用这棵树来调代码。建议在调整时,用printf把case 1到3标记一下,挨个试验。

这颗树的插入顺序是10,7,8,15,5,6,11,13,12。

相关文章
相关标签/搜索