日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

红黑树(二)之 C语言的实现

發布時間:2025/3/20 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 红黑树(二)之 C语言的实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
紅黑樹(二)之 C語言的實現

?

概要

紅黑樹在日常的使用中比較常用,例如Java的TreeMap和TreeSet,C++的STL,以及Linux內核中都有用到。之前寫過一篇文章專門介紹紅黑樹的理論知識,本文將給出紅黑數的C語言的實現代碼,后序章節再分別給出C++和Java版本的實現。還是那句話,三種實現原理相同,擇其一了解即可;若文章有錯誤或不足的地方,望不吝指出!

目錄
1. 紅黑樹的介紹
2. 紅黑樹的C實現(代碼說明)
3. 紅黑樹的C實現(完整源碼)
4. 紅黑樹的C測試程序

轉載請注明出處:http://www.cnblogs.com/skywang12345/p/3624177.html


更多內容數據結構與算法系列 目錄

(01)?紅黑樹(一)之 原理和算法詳細介紹
(02)?紅黑樹(二)之 C語言的實現
(03)?紅黑樹(三)之 Linux內核中紅黑樹的經典實現
(04)?紅黑樹(四)之 C++的實現?
(05)?紅黑樹(五)之 Java的實現
(06)?紅黑樹(六)之 參考資料

?

紅黑樹的介紹

紅黑樹(Red-Black Tree,簡稱R-B Tree),它一種特殊的二叉查找樹。
紅黑樹是特殊的二叉查找樹,意味著它滿足二叉查找樹的特征:任意一個節點所包含的鍵值,大于等于左孩子的鍵值,小于等于右孩子的鍵值。
除了具備該特性之外,紅黑樹還包括許多額外的信息。

紅黑樹的每個節點上都有存儲位表示節點的顏色,顏色是紅(Red)或黑(Black)。
紅黑樹的特性:
(1) 每個節點或者是黑色,或者是紅色。
(2) 根節點是黑色。
(3) 每個葉子節點是黑色。 [注意:這里葉子節點,是指為空的葉子節點!]
(4) 如果一個節點是紅色的,則它的子節點必須是黑色的。
(5) 從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。

關于它的特性,需要注意的是:
第一,特性(3)中的葉子節點,是只為空(NIL或null)的節點。
第二,特性(5),確保沒有一條路徑會比其他路徑長出倆倍。因而,紅黑樹是相對是接近平衡的二叉樹。

紅黑樹示意圖如下:

?

紅黑樹的C實現(代碼說明)

紅黑樹的基本操作是添加刪除旋轉。在對紅黑樹進行添加或刪除后,會用到旋轉方法。為什么呢?道理很簡單,添加或刪除紅黑樹中的節點之后,紅黑樹就發生了變化,可能不滿足紅黑樹的5條性質,也就不再是一顆紅黑樹了,而是一顆普通的樹。而通過旋轉,可以使這顆樹重新成為紅黑樹。簡單點說,旋轉的目的是讓樹保持紅黑樹的特性。
旋轉包括兩種:左旋?和?右旋。下面分別對旋轉(左旋和右旋)、添加、刪除進行介紹。

1. 基本定義

#define RED 0 // 紅色節點 #define BLACK 1 // 黑色節點typedef int Type;// 紅黑樹的節點 typedef struct RBTreeNode{unsigned char color; // 顏色(RED 或 BLACK)Type key; // 關鍵字(鍵值)struct RBTreeNode *left; // 左孩子struct RBTreeNode *right; // 右孩子struct RBTreeNode *parent; // 父結點 }Node, *RBTree;// 紅黑樹的根 typedef struct rb_root{Node *node; }RBRoot;

RBTreeNode是紅黑樹的節點類,RBRoot是紅黑樹的根。

?

2. 左旋

對x進行左旋,意味著"將x變成一個左節點"。

左旋的實現代碼(C語言)

/* * 對紅黑樹的節點(x)進行左旋轉** 左旋示意圖(對節點x進行左旋):* px px* / /* x y * / \ --(左旋)--> / \ #* lx y x ry * / \ / \* ly ry lx ly ***/ static void rbtree_left_rotate(RBRoot *root, Node *x) {// 設置x的右孩子為yNode *y = x->right;// 將 “y的左孩子” 設為 “x的右孩子”;// 如果y的左孩子非空,將 “x” 設為 “y的左孩子的父親”x->right = y->left;if (y->left != NULL)y->left->parent = x;// 將 “x的父親” 設為 “y的父親”y->parent = x->parent;if (x->parent == NULL){//tree = y; // 如果 “x的父親” 是空節點,則將y設為根節點root->node = y; // 如果 “x的父親” 是空節點,則將y設為根節點 }else{if (x->parent->left == x)x->parent->left = y; // 如果 x是它父節點的左孩子,則將y設為“x的父節點的左孩子”elsex->parent->right = y; // 如果 x是它父節點的左孩子,則將y設為“x的父節點的左孩子” }// 將 “x” 設為 “y的左孩子”y->left = x;// 將 “x的父節點” 設為 “y”x->parent = y; }

?

3. 右旋


對y進行左旋,意味著"將y變成一個右節點"。

右旋的實現代碼(C語言)

/* * 對紅黑樹的節點(y)進行右旋轉** 右旋示意圖(對節點y進行左旋):* py py* / /* y x * / \ --(右旋)--> / \ #* x ry lx y * / \ / \ #* lx rx rx ry* */ static void rbtree_right_rotate(RBRoot *root, Node *y) {// 設置x是當前節點的左孩子。Node *x = y->left;// 將 “x的右孩子” 設為 “y的左孩子”;// 如果"x的右孩子"不為空的話,將 “y” 設為 “x的右孩子的父親”y->left = x->right;if (x->right != NULL)x->right->parent = y;// 將 “y的父親” 設為 “x的父親”x->parent = y->parent;if (y->parent == NULL) {//tree = x; // 如果 “y的父親” 是空節點,則將x設為根節點root->node = x; // 如果 “y的父親” 是空節點,則將x設為根節點 }else{if (y == y->parent->right)y->parent->right = x; // 如果 y是它父節點的右孩子,則將x設為“y的父節點的右孩子”elsey->parent->left = x; // (y是它父節點的左孩子) 將x設為“x的父節點的左孩子” }// 將 “y” 設為 “x的右孩子”x->right = y;// 將 “y的父節點” 設為 “x”y->parent = x; }

?

4. 添加

將一個節點(z)插入到紅黑樹中,需要執行哪些步驟呢?首先,將紅黑樹當作一顆二叉查找樹,將節點插入;然后,將節點著色為紅色;最后,通過"旋轉和重新著色"等一系列操作來修正該樹,使之重新成為一顆紅黑樹。詳細描述如下:

第一步: 將紅黑樹當作一顆二叉查找樹,將節點插入。
? ? ? ?紅黑樹本身就是一顆二叉查找樹,將節點插入后,該樹仍然是一顆二叉查找樹。也就意味著,樹的鍵值仍然是有序的。此外,無論是左旋還是右旋,若旋轉之前這棵樹是二叉查找樹,旋轉之后它一定還是二叉查找樹。這也就意味著,任何的旋轉和重新著色操作,都不會改變它仍然是一顆二叉查找樹的事實。
? ? ? ?好吧?那接下來,我們就來想方設法的旋轉以及重新著色,使這顆樹重新成為紅黑樹!

第二步:將插入的節點著色為"紅色"。
? ? ? ?為什么著色成紅色,而不是黑色呢?為什么呢?在回答之前,我們需要重新溫習一下紅黑樹的特性:
(1) 每個節點或者是黑色,或者是紅色。
(2) 根節點是黑色。
(3) 每個葉子節點是黑色。 [注意:這里葉子節點,是指為空的葉子節點!]
(4) 如果一個節點是紅色的,則它的子節點必須是黑色的。
(5) 從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
? ? ? ?將插入的節點著色為紅色,不會違背"特性(5)"!少違背一條特性,就意味著我們需要處理的情況越少。接下來,就要努力的讓這棵樹滿足其它性質即可;滿足了的話,它就又是一顆紅黑樹了。o(∩∩)o...哈哈

第三步: 通過一系列的旋轉或著色等操作,使之重新成為一顆紅黑樹。
? ? ? ?第二步中,將插入節點著色為"紅色"之后,不會違背"特性(5)"。那它到底會違背哪些特性呢?
? ? ? ?對于"特性(1)",顯然不會違背了。因為我們已經將它涂成紅色了。
? ? ? ?對于"特性(2)",顯然也不會違背。在第一步中,我們是將紅黑樹當作二叉查找樹,然后執行的插入操作。而根據二叉查找數的特點,插入操作不會改變根節點。所以,根節點仍然是黑色。
? ? ? ?對于"特性(3)",顯然不會違背了。這里的葉子節點是指的空葉子節點,插入非空節點并不會對它們造成影響。
? ? ? ?對于"特性(4)",是有可能違背的!
那接下來,想辦法使之"滿足特性(4)",就可以將樹重新構造成紅黑樹了。


添加操作的實現代碼(C語言)

/** 添加節點:將節點(node)插入到紅黑樹中** 參數說明:* root 紅黑樹的根* node 插入的結點 // 對應《算法導論》中的z*/ static void rbtree_insert(RBRoot *root, Node *node) {Node *y = NULL;Node *x = root->node;// 1. 將紅黑樹當作一顆二叉查找樹,將節點添加到二叉查找樹中。while (x != NULL){y = x;if (node->key < x->key)x = x->left;elsex = x->right;}rb_parent(node) = y;if (y != NULL){if (node->key < y->key)y->left = node; // 情況2:若“node所包含的值” < “y所包含的值”,則將node設為“y的左孩子”elsey->right = node; // 情況3:(“node所包含的值” >= “y所包含的值”)將node設為“y的右孩子” }else{root->node = node; // 情況1:若y是空節點,則將node設為根 }// 2. 設置節點的顏色為紅色node->color = RED;// 3. 將它重新修正為一顆二叉查找樹 rbtree_insert_fixup(root, node); }

rbtree_insert(root, node)的作用是將"node"節點插入到紅黑樹中。其中,root是根,node是被插入節點。
rbtree_insert(root, node)是參考《算法導論》中紅黑樹的插入函數的偽代碼進行實現的。


添加修正操作的實現代碼(C語言)

/** 紅黑樹插入修正函數** 在向紅黑樹中插入節點之后(失去平衡),再調用該函數;* 目的是將它重新塑造成一顆紅黑樹。** 參數說明:* root 紅黑樹的根* node 插入的結點 // 對應《算法導論》中的z*/ static void rbtree_insert_fixup(RBRoot *root, Node *node) {Node *parent, *gparent;// 若“父節點存在,并且父節點的顏色是紅色”while ((parent = rb_parent(node)) && rb_is_red(parent)){gparent = rb_parent(parent);//若“父節點”是“祖父節點的左孩子”if (parent == gparent->left){// Case 1條件:叔叔節點是紅色 {Node *uncle = gparent->right;if (uncle && rb_is_red(uncle)){rb_set_black(uncle);rb_set_black(parent);rb_set_red(gparent);node = gparent;continue;}}// Case 2條件:叔叔是黑色,且當前節點是右孩子if (parent->right == node){Node *tmp;rbtree_left_rotate(root, parent);tmp = parent;parent = node;node = tmp;}// Case 3條件:叔叔是黑色,且當前節點是左孩子。 rb_set_black(parent);rb_set_red(gparent);rbtree_right_rotate(root, gparent);} else//若“z的父節點”是“z的祖父節點的右孩子” {// Case 1條件:叔叔節點是紅色 {Node *uncle = gparent->left;if (uncle && rb_is_red(uncle)){rb_set_black(uncle);rb_set_black(parent);rb_set_red(gparent);node = gparent;continue;}}// Case 2條件:叔叔是黑色,且當前節點是左孩子if (parent->left == node){Node *tmp;rbtree_right_rotate(root, parent);tmp = parent;parent = node;node = tmp;}// Case 3條件:叔叔是黑色,且當前節點是右孩子。 rb_set_black(parent);rb_set_red(gparent);rbtree_left_rotate(root, gparent);}}// 將根節點設為黑色rb_set_black(root->node); }

rbtree_insert_fixup(root, node)的作用是對應"上面所講的第三步"。

?

5. 刪除操作

將紅黑樹內的某一個節點刪除。需要執行的操作依次是:首先,將紅黑樹當作一顆二叉查找樹,將該節點從二叉查找樹中刪除;然后,通過"旋轉和重新著色"等一系列來修正該樹,使之重新成為一棵紅黑樹。詳細描述如下:

第一步:將紅黑樹當作一顆二叉查找樹,將節點刪除。
? ? ? ?這和"刪除常規二叉查找樹中刪除節點的方法是一樣的"。分3種情況:
① 被刪除節點沒有兒子,即為葉節點。那么,直接將該節點刪除就OK了。
② 被刪除節點只有一個兒子。那么,直接刪除該節點,并用該節點的唯一子節點頂替它的位置。
③ 被刪除節點有兩個兒子。那么,先找出它的后繼節點;然后把“它的后繼節點的內容”復制給“該節點的內容”;之后,刪除“它的后繼節點”。在這里,后繼節點相當于替身,在將后繼節點的內容復制給"被刪除節點"之后,再將后繼節點刪除。這樣就巧妙的將問題轉換為"刪除后繼節點"的情況了,下面就考慮后繼節點。 在"被刪除節點"有兩個非空子節點的情況下,它的后繼節點不可能是雙子非空。既然"的后繼節點"不可能雙子都非空,就意味著"該節點的后繼節點"要么沒有兒子,要么只有一個兒子。若沒有兒子,則按"情況① "進行處理;若只有一個兒子,則按"情況② "進行處理。

第二步:通過"旋轉和重新著色"等一系列來修正該樹,使之重新成為一棵紅黑樹。
因為"第一步"中刪除節點之后,可能會違背紅黑樹的特性。所以需要通過"旋轉和重新著色"來修正該樹,使之重新成為一棵紅黑樹。


刪除操作的實現代碼(C語言)

/* * 刪除結點** 參數說明:* tree 紅黑樹的根結點* node 刪除的結點*/ void rbtree_delete(RBRoot *root, Node *node) {Node *child, *parent;int color;// 被刪除節點的"左右孩子都不為空"的情況。if ( (node->left!=NULL) && (node->right!=NULL) ) {// 被刪節點的后繼節點。(稱為"取代節點")// 用它來取代"被刪節點"的位置,然后再將"被刪節點"去掉。Node *replace = node;// 獲取后繼節點replace = replace->right;while (replace->left != NULL)replace = replace->left;// "node節點"不是根節點(只有根節點不存在父節點)if (rb_parent(node)){if (rb_parent(node)->left == node)rb_parent(node)->left = replace;elserb_parent(node)->right = replace;} else // "node節點"是根節點,更新根節點。root->node = replace;// child是"取代節點"的右孩子,也是需要"調整的節點"。// "取代節點"肯定不存在左孩子!因為它是一個后繼節點。child = replace->right;parent = rb_parent(replace);// 保存"取代節點"的顏色color = rb_color(replace);// "被刪除節點"是"它的后繼節點的父節點"if (parent == node){parent = replace;} else{// child不為空if (child)rb_set_parent(child, parent);parent->left = child;replace->right = node->right;rb_set_parent(node->right, replace);}replace->parent = node->parent;replace->color = node->color;replace->left = node->left;node->left->parent = replace;if (color == BLACK)rbtree_delete_fixup(root, child, parent);free(node);return ;}if (node->left !=NULL)child = node->left;else child = node->right;parent = node->parent;// 保存"取代節點"的顏色color = node->color;if (child)child->parent = parent;// "node節點"不是根節點if (parent){if (parent->left == node)parent->left = child;elseparent->right = child;}elseroot->node = child;if (color == BLACK)rbtree_delete_fixup(root, child, parent);free(node); }

rbtree_delete(root, node)的作用是將"node"節點插入到紅黑樹中。其中,root是根,node是被插入節點。


刪除修正操作的實現代碼(C語言)

/** 紅黑樹刪除修正函數** 在從紅黑樹中刪除插入節點之后(紅黑樹失去平衡),再調用該函數;* 目的是將它重新塑造成一顆紅黑樹。** 參數說明:* root 紅黑樹的根* node 待修正的節點*/ static void rbtree_delete_fixup(RBRoot *root, Node *node, Node *parent) {Node *other;while ((!node || rb_is_black(node)) && node != root->node){if (parent->left == node){other = parent->right;if (rb_is_red(other)){// Case 1: x的兄弟w是紅色的 rb_set_black(other);rb_set_red(parent);rbtree_left_rotate(root, parent);other = parent->right;}if ((!other->left || rb_is_black(other->left)) &&(!other->right || rb_is_black(other->right))){// Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的 rb_set_red(other);node = parent;parent = rb_parent(node);}else{if (!other->right || rb_is_black(other->right)){// Case 3: x的兄弟w是黑色的,并且w的左孩子是紅色,右孩子為黑色。 rb_set_black(other->left);rb_set_red(other);rbtree_right_rotate(root, other);other = parent->right;}// Case 4: x的兄弟w是黑色的;并且w的右孩子是紅色的,左孩子任意顏色。 rb_set_color(other, rb_color(parent));rb_set_black(parent);rb_set_black(other->right);rbtree_left_rotate(root, parent);node = root->node;break;}}else{other = parent->left;if (rb_is_red(other)){// Case 1: x的兄弟w是紅色的 rb_set_black(other);rb_set_red(parent);rbtree_right_rotate(root, parent);other = parent->left;}if ((!other->left || rb_is_black(other->left)) &&(!other->right || rb_is_black(other->right))){// Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的 rb_set_red(other);node = parent;parent = rb_parent(node);}else{if (!other->left || rb_is_black(other->left)){// Case 3: x的兄弟w是黑色的,并且w的左孩子是紅色,右孩子為黑色。 rb_set_black(other->right);rb_set_red(other);rbtree_left_rotate(root, other);other = parent->left;}// Case 4: x的兄弟w是黑色的;并且w的右孩子是紅色的,左孩子任意顏色。 rb_set_color(other, rb_color(parent));rb_set_black(parent);rb_set_black(other->left);rbtree_right_rotate(root, parent);node = root->node;break;}}}if (node)rb_set_black(node); }

rbtree_delete_fixup(root, node, parent)是對應"上面所講的第三步"。

?

紅黑樹的C實現(完整源碼)

下面是紅黑數實現的完整代碼和相應的測試程序。
(1) 除了上面所說的"左旋"、"右旋"、"添加"、"刪除"等基本操作之后,還實現了"遍歷"、"查找"、"打印"、"最小值"、"最大值"、"創建"、"銷毀"等接口。
(2) 函數接口分為內部接口和外部接口。內部接口是static函數,外部接口則是非static函數,外部接口都在.h頭文件中表明了。
(3) 測試代碼中提供了"插入"和"刪除"動作的檢測開關。默認是關閉的,打開方法可以參考"代碼中的說明"。建議在打開開關后,在草稿上自己動手繪制一下紅黑樹。

紅黑樹的實現文件(rbtree.h)

?View Code

紅黑樹的實現文件(rbtree.c)

?View Code

紅黑樹的測試文件(rbtree_test.c)

?View Code

?

紅黑樹的C測試程序

前面已經給出了紅黑樹的測試程序(rbtree_test.c),這里就不再重復說明。下面是測試程序的運行結果:

== 原始數據: 10 40 30 60 90 70 20 50 80 == 前序遍歷: 30 10 20 60 40 50 80 70 90 == 中序遍歷: 10 20 30 40 50 60 70 80 90 == 后序遍歷: 20 10 50 40 70 90 80 60 30 == 最小值: 10 == 最大值: 90 == 樹的詳細信息: 30(B) is root 10(B) is 30's left child 20(R) is 10's right child 60(R) is 30's right child 40(B) is 60's left child 50(R) is 40's right child 80(B) is 60's right child 70(R) is 80's left child 90(R) is 80's right child

總結

以上是生活随笔為你收集整理的红黑树(二)之 C语言的实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。