接下來介紹紅黑樹的插入操做,介紹插入以前,咱們先來了解一下紅黑樹的性質。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。