這段時間,天天解bug,搞的整個人都煩死了。突然,對紅黑樹的實現來了興趣,就費了3天的功夫,自己作了一個,雖然很簡單,但實現起來,還真要費點勁。
紅黑樹的性質很簡單,要滿足以下5條。
1) 每個節點必須是紅色或者是黑色的;
2) 所有的葉子節點的顏色都是黑色的;
3) 如果有一個節點顏色是紅色的,那麼它的兒子節點顏色是黑的,它的父親節點顏色也是黑的;
4) 任一節點到該節點上的葉子的所有簡單路徑上的黑色節點的個數是相同的;
5) 根節點的顏色是黑色的;
在紅黑樹的操作中,左旋和右旋是比較常用的操作。實現如下:
/*
下面的操作叫左旋操作,可以這樣記憶: 在P1的子樹下,最終B成爲根,代替了A,所以,所謂左旋是針對B的方向來稱呼的。以下右旋雷同;因爲是A樹下的左旋,左邊旋轉是對B來講,所以B必是A的右孩子;
p1 p2
| |
| |
( A ) ( B )
/ / / /
/ / LR / /
a ( B ) =======> (A) c
/ / / /
/ / / /
b c a b
*/
void LeftRotate(PNODE* root, PNODE A)
{
PNODE B = A->right;
if (B == LEAF)
{
return;
}
A->right = B->left;
if (B->left != LEAF)
{
(B->left)->parent = A;
}
B->parent = A->parent;
if (A == *root)
{
*root = B;
}
else if (A == ((A->parent)->left))
{
A->parent->left = B;
}
else
{
A->parent->right = B;
}
B->left = A;
A->parent = B;
}
/*
下面是右旋函數,與上面的函數相對應;
| |
| |
( A ) ( B )
/ / / /
/ / RR / /
( B ) a =======> b ( A )
/ / / /
/ / / /
b c c a
*/
void RightRotate(PNODE* root, PNODE A)
{
PNODE B = A->left;
if (B == LEAF)
{
return;
}
A->left = B->right;
if (B->right != LEAF)
{
(B->right)->parent = A;
}
B->parent = A->parent;
if (A == *root)
{
*root = B;
}
else if (A == ((A->parent)->left))
{
(A->parent)->left = B;
}
else
{
(A->parent)->right = B;
}
B->right = A;
A->parent = B;
}
一下是詳細的代碼和註釋:
/*
* 顏色的枚舉類型
*/
enum COLOR
{
RED = 0,
BLACK
};
/*
* 簡單的定義一下數據結構
*/
#define ELEMENT int
/*
* 節點的數據結構
*/
typedef struct TAG_NODE
{
TAG_NODE* parent;
TAG_NODE* left;
TAG_NODE* right;
COLOR color;
ELEMENT key;
} NODE, *PNODE;
/*
* 實現對樹的非遞歸遍歷是利用的虛擬棧數據結構
*/
typedef struct TAG_SQ
{
PNODE data[2048];
int index; /* from 1 - 2047 */
int size; /* the max size = 2047 */
} SQ, *PSQ;
#define LEAF ((PNODE)0)
#define INT32 int
#define Parent(x) ((x)->parent)
#define Grand(x) ( ((x) ->parent)->parent)
#define Brother(x) ( ( (x) == ( (x)->parent->left) ) ? ( (x)->parent->right) : ( (x)->parent->left) )
#define Uncle(x) Brother( (Parent(x) ) )
/*
* 此宏默認葉子節點的顏色是黑色
*/
#define GetColor(x) ((x) == LEAF ? BLACK : (x)->color)
void LeftRotate(PNODE* root, PNODE A);
void RightRotate(PNODE* root, PNODE A);
PNODE FindNode(PNODE* root, ELEMENT key);
PNODE FindNextNode(PNODE* root, ELEMENT key, PNODE start);
PNODE InsertNode(PNODE* root, ELEMENT key);
PNODE DeleteNode(PNODE* root, ELEMENT key);
PNODE FixupTreeByInsert(PNODE* root, PNODE z);
void FixupTreeByDelete(PNODE* root, PNODE z);
/*
* 保存紅黑樹的根節點
*/
PNODE root = LEAF;
void LeftRotate(PNODE* root, PNODE A)
{
PNODE B = A->right;
if (B == LEAF)
{
return;
}
A->right = B->left;
if (B->left != LEAF)
{
(B->left)->parent = A;
}
B->parent = A->parent;
if (A == *root)
{
*root = B;
}
else if (A == ((A->parent)->left))
{
A->parent->left = B;
}
else
{
A->parent->right = B;
}
B->left = A;
A->parent = B;
}
void RightRotate(PNODE* root, PNODE A)
{
PNODE B = A->left;
if (B == LEAF)
{
return;
}
A->left = B->right;
if (B->right != LEAF)
{
(B->right)->parent = A;
}
B->parent = A->parent;
if (A == *root)
{
*root = B;
}
else if (A == ((A->parent)->left))
{
(A->parent)->left = B;
}
else
{
(A->parent)->right = B;
}
B->right = A;
A->parent = B;
}
/*
* 對給定的紅黑樹,找到存貯有特定key值的節點;
*/
PNODE FindNode(PNODE* root, ELEMENT key)
{
PNODE temp = *root;
PNODE track = *root;
while (temp)
{
track = temp;
if (key == temp->key)
{
break;
}
else if (key < (temp->key))
{
temp = temp->left;
}
else
{
temp = temp->right;
}
}
return track;
}
/*
* 找到比給定值 key 大的最少的節點
*/
PNODE FindNextNode(PNODE* root, ELEMENT key, PNODE start)
{
PNODE track = start;
if ( (track == LEAF) || (track->key != key) )
{
track = FindNode(root, key);
}
PNODE x = track;
if (track && track->right)
{
track = track->right;
while (track)
{
x = track;
track = track->left;
}
}
return x;
}
PNODE InsertNode(PNODE* root, ELEMENT key)
{
PNODE x, y;
PNODE z;
/*
* z: new node wanted to insert;
*/
z = (PNODE)malloc(sizeof(NODE));
if (z == NULL)
{
printf("can't alloc memory for new node /n");
return NULL;
}
x = *root;
/*
* y: keep track for the new node's parent;
*/
y = LEAF;
while (x != LEAF)
{
y = x;
if (key == (x->key))
{
printf("key has been confilct /n");
return NULL;
}
else if (key < (x->key))
{
x = x->left;
}
else
{
x = x->right;
}
}
z->parent = y;
if (y == LEAF)
{
*root = z;
}
else
{
if ( key < (y->key))
{
y->left = z;
}
else
{
y->right = z;
}
}
z->left = LEAF;
z->right = LEAF;
z->color = RED;
z->key = key;
return FixupTreeByInsert(root, z);
}
/*
增加節點,最開始,節點的顏色都設置爲紅色,有兩種是簡單的情況:
1) 插入的是根節點, 直接把節點顏色改成黑色即可;
2) 插入節點的父節點是黑色的,直接插入即可;
3) 父親節點是紅色的時候需要調整,此時祖父節點肯定存在(因爲根節點顏色是黑色,所以父親節點不可能是根節點)且顏色爲黑色,否則跟父親節點顏色衝突。
a. 父親是紅色,祖父是黑色,叔叔是紅色,此時處理如下:
G(b) G(r)
/ / / /
/ / / /
P(r) U(r) P(b) U(b)
/ /
/ /
N(r) N(r)
這樣,從左變到右, 衝突的節點從 N 轉移到了 G, 這樣從G從新調整紅黑樹;
b. 父親是紅色,祖父是黑色,叔叔是黑色
這裏分兩種情況,一是父親節點是爺爺的左兒子,二是父親節點是爺爺的右兒子,這麼分是與這兩種情況下調整策略是相對的。
僅以父親節點是爺爺節點的左兒子來說明:
此時又分兩種情況, 一是新加節點是父親節點的左兒子,二是新加節點是父親節點的右兒子。
b-1 : 是右兒子的調整如下:
G(b) G(b)
/ / / /
/ / / /
P(r) U(b) N(r) U(b)
/ /
/ /
N(r) P(r)
對P進行左旋後, 形成右邊的局勢,如果將 P視爲原來的 N的話,現在變成了 b-2 這種情況;
b-2 : 是左兒子的調整如下:
G(b) P(b)
/ / / /
/ / / /
P(r) U(b) N(r) G(r)
/ /
/ /
N(r) U(b)
對G做右旋轉之後,N節點的父親變成黑色的,調整完成。
*/
PNODE FixupTreeByInsert(PNODE* root, PNODE z)
{
PNODE x = z;
while ( (x != (*root)) && ( Parent(x)->color == RED ))
{
if ( Uncle(x) && Uncle(x)->color == RED )
{
Parent(x)->color = BLACK;
Uncle(x)->color = BLACK;
Grand(x)->color = RED;
x = Grand(x);
}
else
{
if ( Parent(x) == Grand(x)->left )
{
if ( x == Parent(x)->right )
{
LeftRotate(root, Parent(x));
x = x->left;
}
Parent(x)->color = BLACK;
Grand(x)->color = RED;
RightRotate(root, Grand(x));
}
else
{
if (x == Parent(x)->left )
{
RightRotate(root, Parent(x));
x = x->right;
}
Parent(x)->color = BLACK;
Grand(x)->color = RED;
LeftRotate(root, Grand(x));
}
}
}
(*root)->color = BLACK;
return (*root);
}
PNODE DeleteNode(PNODE* root, ELEMENT key)
{
PNODE x;
/*
* 找到刪去節點的位置
*/
x = FindNode(root, key);
if (x == LEAF)
{
return LEAF;
}
PNODE y;
/*
* 找到整個序列中在刪除節點後面的節點,用它替換要刪的節點,然後把這個節點刪去
*/
y = FindNextNode(root, key, x);
x->key = y->key;
/*
* will delete y;
*/
FixupTreeByDelete(root, y);
/*
* del black sun;
*/
if (y->left)
{
y->key = y->left->key;
free(y->left);
y->left = LEAF;
y->color = BLACK;
}
else if (y->right)
{
y->key = y->right->key;
free(y->right);
y->right = LEAF;
y->color = BLACK;
}
else
{
if (y != *root)
{
if (y == Parent(y)->left)
{
Parent(y)->left = LEAF;
}
else
{
Parent(y)->right = LEAF;
}
}
else
{
*root = LEAF;
}
free(y);
}
return x;
}
/*
刪去節點的調整操作如下:
首先,有兩種簡單情況。
1) 要刪去的節點顏色是紅色的,這樣它的父親和兒子都是黑色的,讓它的兒子直接代替它就可以了;
2) 要刪去的節點顏色是黑色的,但是它有一個紅色的兒子,這樣將兒子顏色編程黑色,直接代替它就可以了;
剩下的情況是要進行調整的:
刪去的節點顏色是黑色的,它的兒子顏色都是黑色的。(如果兒子爲null, 那麼視爲顏色是黑色的)
以下的操作分兩種情況,一是刪去的節點是父親的左兒子,二是刪去的節點是父親的右兒子,這兩種情況下做的操作是相對的,下面以是左兒子這種情況說明.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
a): 兄弟節點是紅色的,這樣,父親顏色是黑色,兄弟節點的兒子節點顏色是黑的;
P(b) S(b)
/ / / /
/ / / /
N(b) S(r) P(r) Sr(b)
/ / / /
/ / / /
Sl(b) Sr(b) N(b) Sl(b)
對P作左旋操作,完成之後, N節點的兄弟顏色變成了黑色,轉化成下面的情況之一;
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
b): 兄弟節點是黑色的。
/* 以 Sl 稱呼兄弟節點的左兒子, Sr 稱呼兄弟節點的右兒子 */
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
b-1): Sl = b, Sr = b;
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
b-1-1) 父親節點是黑色的;
P(b) P(b)
/ / / /
/ / / /
N(b) S(b) N(b) S(r)
/ / / /
/ / / /
Sl(b) Sr(b) Sl(b) Sr(b)
將 S 的顏色變成紅色的,這樣在刪去 N 後, 通過 P 的路徑上的黑色節點會少一個,變成了以 P爲基點再作一輪調整,注意,這裏分類是以兄弟節點的顏色來區分的,是因爲不管怎麼調整,兄弟節點原來都是具有紅黑樹的性質,可以作出當兄弟節點是紅色的,父親節點和兄弟節點兒子顏色是黑色這樣的判斷。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
b-1-2) 父親節點是紅色的;
P(r) P(b)
/ / / /
/ / / /
N(b) S(b) N(b) S(r)
/ / / /
/ / / /
Sl(b) Sr(b) Sl(b) Sr(b)
通過以上的調整, P-N 這條路徑多一個黑色的節點,把N刪去之後剛好,調整結束;
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
b-2): Sl = r, Sr = b;
S(b) Sl(b)
/ / / /
/ / / /
Sl(r) Sr(b) # S(r)
/ /
/ /
# Sr(b)
對S節點右旋之後, #肯定是黑色的, 這樣N的新的兄弟節點就是原來的 Sl 節點,這樣變成了兄弟節點的左兒子是黑色的,右兒子是紅色的,變成了情況 b-3);
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
b-3): Sl = (r/b) Sr = r;
P(r/b) S(r/b)
/ / / /
/ / / /
N(b) S(b) P(b) Sr(b)
/ /
/ /
Sr(r) N(b)
將 P 作一個左旋, S 顏色和P顏色交換, Sr 顏色變成紅色, 可以驗證刪去 N 後,剛好保持了紅黑樹的性質,所以調整到此結束
*/
void FixupTreeByDelete(PNODE* root, PNODE z)
{
PNODE del = z;
if ( (del->color == RED) ||
(del->left && del->left->color == RED) ||
(del->right && del->right->color == RED) )
{
return;
}
while (z != (*root) )
{
if ( Brother(z) == LEAF)
{
z = Parent(z);
}
else if ( (Brother(z)->color == BLACK) &&
(Parent(z)->color == BLACK) &&
(GetColor(Brother(z)->left) == BLACK) &&
(GetColor(Brother(z)->right) == BLACK) )
{
Brother(z)->color = RED;
z = Parent(z);
}
else
{
if (z == Parent(z)->left)
{
if (Brother(z)->color == RED)
{
/*
* 將父親的顏色變成黑色;
*/
Parent(z)->color = RED;
Brother(z)->color = BLACK;
LeftRotate(root, Parent(z));
}
else if ( Parent(z)->color == RED &&
GetColor(Brother(z)->left) == BLACK &&
GetColor(Brother(z)->right) == BLACK )
{
Parent(z)->color = BLACK;
Brother(z)->color = RED;
break;
}
else if ( GetColor(Brother(z)->left) == RED &&
GetColor(Brother(z)->right) == BLACK)
{
Brother(z)->color = RED;
Brother(z)->left->color = BLACK;
RightRotate(root, Brother(z));
}
else if ( GetColor(Brother(z)->right) == RED)
{
Brother(z)->color = Parent(z)->color;
Parent(z)->color = BLACK;
Brother(z)->right->color = BLACK;
LeftRotate(root, Parent(z));
break;
}
else
{
printf("bad case comes here ......../n");
}
}
else
{
if (Brother(z)->color == RED)
{
Brother(z)->color = BLACK;
Parent(z)->color = RED;
RightRotate(root, Parent(z));
}
else if ( (Parent(z)->color == RED) &&
(GetColor(Brother(z)->left) == BLACK) &&
(GetColor(Brother(z)->right) == BLACK) )
{
Parent(z)->color = BLACK;
Brother(z)->color = RED;
break;
}
else if ( GetColor(Brother(z)->left) == BLACK &&
GetColor(Brother(z)->right) == RED )
{
Brother(z)->color = RED;
Brother(z)->right->color = BLACK;
LeftRotate(root, Brother(z));
}
else if ( GetColor(Brother(z)->left) == RED)
{
Brother(z)->color = Parent(z)->color;
Parent(z)->color = BLACK;
Brother(z)->left->color = BLACK;
RightRotate(root, Parent(z));
break;
}
else
{
printf("bad case ... /n");
}
}
}
}
}
PSQ SQ_Init(void)
{
PSQ q = (PSQ)malloc(sizeof(SQ));
if (q != NULL)
{
memset(q, 0, sizeof(SQ));
q->index = 0;
q->size = 2047;
}
return q;
}
void SQ_Destory(PSQ q)
{
if (q)
{
free(q);
}
}
int SQ_Push(PSQ q, PNODE n)
{
if (q->index >= q->size )
{
printf("stack over flow .. /n");
return 0;
}
q->index++;
q->data[q->index] = n;
}
int SQ_IsEmpty(PSQ q)
{
if (q->index <= 0)
{
return 1;
}
return 0;
}
PNODE SQ_Pop(PSQ q)
{
if ( SQ_IsEmpty(q))
{
printf("no other element in stack .... /n");
return LEAF;
}
PNODE ret = q->data[q->index];
q->index--;
return ret;
}
PNODE SQ_Top(PSQ q)
{
return q->data[q->index];
}
void VisitNode(PNODE n)
{
printf("[key = %d] /n", n->key);
}
void TravelTheTree(PNODE* root)
{
PNODE p = (*root);
PSQ sq = NULL;
sq = SQ_Init();
while (p || (!SQ_IsEmpty(sq)))
{
while (p)
{
SQ_Push(sq, p);
p = p->left;
}
p = SQ_Pop(sq);
VisitNode(p);
p = p->right;
}
SQ_Destory(sq);
}
int _tmain(int argc, _TCHAR* argv[])
{
int Data[] = {4, 8 ,2, 1, 3 ,5, 29, 12, 17, 33 };
int Data_Del[] = {1,2,3,4,5,8,12,17,29,33};
for (int i = 0; i < sizeof(Data)/sizeof(int); i++)
{
InsertNode(&root, Data[i]);
}
printf("after insert element: /n");
TravelTheTree(&root);
for (int i = 0; i < sizeof(Data)/sizeof(int); i++)
{
DeleteNode(&root, Data[i]);
printf("delete key = %d /n", Data[i]);
printf("===================================== /n");
TravelTheTree(&root);
}
return 0;
}
相關參考見: http://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91