紅黑樹(二)插入

 接下來介紹紅黑樹的插入操作,介紹插入之前,我們先來了解一下紅黑樹的性質。

  1、每個節點不是紅色就是黑色

  2、跟節點爲黑色。

  3、如果節點爲紅,子節點必須爲黑。

  4、任意節點至樹尾端的任何路徑,黑節點必須相同。

  

規則4主要是保證樹的平衡性,不過它的要求不是很嚴。主要是爲了減少調整操作。根據規則4,我們可以判斷出新節點都是紅節點。(如果新節點是黑節點,那麼每次插入都要進行調整)

由於要經常使用某個節點的父親。所以這裏添加了一個指向父親的指針。所以我們先看一下紅黑樹的結構組成。

複製代碼
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只有一個根節點。

插入操作,與二叉樹的插入是一樣的。

首先是找到插入位置。

複製代碼
//根節點返回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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章