红黑树(三)之 C++的实现
概要
前面分別介紹紅黑樹的理論知識(shí)和紅黑樹的C語(yǔ)言實(shí)現(xiàn)。本章是紅黑樹的C++實(shí)現(xiàn),若讀者對(duì)紅黑樹的理論知識(shí)不熟悉,建立先學(xué)習(xí)紅黑樹的理論知識(shí),再來(lái)學(xué)習(xí)本章。
目錄
1.?紅黑樹的介紹
2. 紅黑樹的C++實(shí)現(xiàn)(代碼說(shuō)明)
3. 紅黑樹的C++實(shí)現(xiàn)(完整源碼)
4. 紅黑樹的C++測(cè)試程序
轉(zhuǎn)載請(qǐng)注明出處:http://www.cnblogs.com/skywang12345/p/3624291.html
更多內(nèi)容:數(shù)據(jù)結(jié)構(gòu)與算法系列 目錄
(01)?紅黑樹(一)之 原理和算法詳細(xì)介紹
(02)?紅黑樹(二)之 C語(yǔ)言的實(shí)現(xiàn)
(03)?紅黑樹(三)之 C++的實(shí)現(xiàn)?
?
紅黑樹的介紹
紅黑樹(Red-Black Tree,簡(jiǎn)稱R-B Tree),它一種特殊的二叉查找樹。
紅黑樹是特殊的二叉查找樹,意味著它滿足二叉查找樹的特征:任意一個(gè)節(jié)點(diǎn)所包含的鍵值,大于等于左孩子的鍵值,小于等于右孩子的鍵值。
除了具備該特性之外,紅黑樹還包括許多額外的信息。
紅黑樹的每個(gè)節(jié)點(diǎn)上都有存儲(chǔ)位表示節(jié)點(diǎn)的顏色,顏色是紅(Red)或黑(Black)。
紅黑樹的特性:
(1) 每個(gè)節(jié)點(diǎn)或者是黑色,或者是紅色。
(2) 根節(jié)點(diǎn)是黑色。
(3) 每個(gè)葉子節(jié)點(diǎn)是黑色。 [注意:這里葉子節(jié)點(diǎn),是指為空的葉子節(jié)點(diǎn)!]
(4) 如果一個(gè)節(jié)點(diǎn)是紅色的,則它的子節(jié)點(diǎn)必須是黑色的。
(5) 從一個(gè)節(jié)點(diǎn)到該節(jié)點(diǎn)的子孫節(jié)點(diǎn)的所有路徑上包含相同數(shù)目的黑節(jié)點(diǎn)。
關(guān)于它的特性,需要注意的是:
第一,特性(3)中的葉子節(jié)點(diǎn),是只為空(NIL或null)的節(jié)點(diǎn)。
第二,特性(5),確保沒(méi)有一條路徑會(huì)比其他路徑長(zhǎng)出倆倍。因而,紅黑樹是相對(duì)是接近平衡的二叉樹。
紅黑樹示意圖如下:
?
紅黑樹的C++實(shí)現(xiàn)(代碼說(shuō)明)
紅黑樹的基本操作是添加、刪除和旋轉(zhuǎn)。在對(duì)紅黑樹進(jìn)行添加或刪除后,會(huì)用到旋轉(zhuǎn)方法。為什么呢?道理很簡(jiǎn)單,添加或刪除紅黑樹中的節(jié)點(diǎn)之后,紅黑樹就發(fā)生了變化,可能不滿足紅黑樹的5條性質(zhì),也就不再是一顆紅黑樹了,而是一顆普通的樹。而通過(guò)旋轉(zhuǎn),可以使這顆樹重新成為紅黑樹。簡(jiǎn)單點(diǎn)說(shuō),旋轉(zhuǎn)的目的是讓樹保持紅黑樹的特性。
旋轉(zhuǎn)包括兩種:左旋 和 右旋。下面分別對(duì)紅黑樹的基本操作進(jìn)行介紹。
?
1. 基本定義
enum RBTColor{RED, BLACK};template <class T> class RBTNode{public:RBTColor color; // 顏色T key; // 關(guān)鍵字(鍵值)RBTNode *left; // 左孩子RBTNode *right; // 右孩子RBTNode *parent; // 父結(jié)點(diǎn) RBTNode(T value, RBTColor c, RBTNode *p, RBTNode *l, RBTNode *r):key(value),color(c),parent(),left(l),right(r) {} };template <class T> class RBTree {private:RBTNode<T> *mRoot; // 根結(jié)點(diǎn)public:RBTree();~RBTree();// 前序遍歷"紅黑樹"void preOrder();// 中序遍歷"紅黑樹"void inOrder();// 后序遍歷"紅黑樹"void postOrder();// (遞歸實(shí)現(xiàn))查找"紅黑樹"中鍵值為key的節(jié)點(diǎn)RBTNode<T>* search(T key);// (非遞歸實(shí)現(xiàn))查找"紅黑樹"中鍵值為key的節(jié)點(diǎn)RBTNode<T>* iterativeSearch(T key);// 查找最小結(jié)點(diǎn):返回最小結(jié)點(diǎn)的鍵值。 T minimum();// 查找最大結(jié)點(diǎn):返回最大結(jié)點(diǎn)的鍵值。 T maximum();// 找結(jié)點(diǎn)(x)的后繼結(jié)點(diǎn)。即,查找"紅黑樹中數(shù)據(jù)值大于該結(jié)點(diǎn)"的"最小結(jié)點(diǎn)"。RBTNode<T>* successor(RBTNode<T> *x);// 找結(jié)點(diǎn)(x)的前驅(qū)結(jié)點(diǎn)。即,查找"紅黑樹中數(shù)據(jù)值小于該結(jié)點(diǎn)"的"最大結(jié)點(diǎn)"。RBTNode<T>* predecessor(RBTNode<T> *x);// 將結(jié)點(diǎn)(key為節(jié)點(diǎn)鍵值)插入到紅黑樹中void insert(T key);// 刪除結(jié)點(diǎn)(key為節(jié)點(diǎn)鍵值)void remove(T key);// 銷毀紅黑樹void destroy();// 打印紅黑樹void print();private:// 前序遍歷"紅黑樹"void preOrder(RBTNode<T>* tree) const;// 中序遍歷"紅黑樹"void inOrder(RBTNode<T>* tree) const;// 后序遍歷"紅黑樹"void postOrder(RBTNode<T>* tree) const;// (遞歸實(shí)現(xiàn))查找"紅黑樹x"中鍵值為key的節(jié)點(diǎn)RBTNode<T>* search(RBTNode<T>* x, T key) const;// (非遞歸實(shí)現(xiàn))查找"紅黑樹x"中鍵值為key的節(jié)點(diǎn)RBTNode<T>* iterativeSearch(RBTNode<T>* x, T key) const;// 查找最小結(jié)點(diǎn):返回tree為根結(jié)點(diǎn)的紅黑樹的最小結(jié)點(diǎn)。RBTNode<T>* minimum(RBTNode<T>* tree);// 查找最大結(jié)點(diǎn):返回tree為根結(jié)點(diǎn)的紅黑樹的最大結(jié)點(diǎn)。RBTNode<T>* maximum(RBTNode<T>* tree);// 左旋void leftRotate(RBTNode<T>* &root, RBTNode<T>* x);// 右旋void rightRotate(RBTNode<T>* &root, RBTNode<T>* y);// 插入函數(shù)void insert(RBTNode<T>* &root, RBTNode<T>* node);// 插入修正函數(shù)void insertFixUp(RBTNode<T>* &root, RBTNode<T>* node);// 刪除函數(shù)void remove(RBTNode<T>* &root, RBTNode<T> *node);// 刪除修正函數(shù)void removeFixUp(RBTNode<T>* &root, RBTNode<T> *node, RBTNode<T> *parent);// 銷毀紅黑樹void destroy(RBTNode<T>* &tree);// 打印紅黑樹void print(RBTNode<T>* tree, T key, int direction);#define rb_parent(r) ((r)->parent) #define rb_color(r) ((r)->color) #define rb_is_red(r) ((r)->color==RED) #define rb_is_black(r) ((r)->color==BLACK) #define rb_set_black(r) do { (r)->color = BLACK; } while (0) #define rb_set_red(r) do { (r)->color = RED; } while (0) #define rb_set_parent(r,p) do { (r)->parent = (p); } while (0) #define rb_set_color(r,c) do { (r)->color = (c); } while (0) }; RBTNode是紅黑樹的節(jié)點(diǎn)類,而RBTree對(duì)應(yīng)是紅黑樹的操作實(shí)現(xiàn)類。在RBTree中包含了根節(jié)點(diǎn)mRoot和紅黑樹的相關(guān)API。
注意:(01) 在實(shí)現(xiàn)紅黑樹API的過(guò)程中,我重載了許多函數(shù)。重載的原因,一是因?yàn)橛械腁PI是內(nèi)部接口,有的是外部接口;二是為了讓結(jié)構(gòu)更加清晰。
? ? ? ? ? (02) 由于C++的實(shí)現(xiàn)是在上一篇介紹的"C語(yǔ)言"實(shí)現(xiàn)基礎(chǔ)上移植而來(lái),在該代碼中,保留了一些C語(yǔ)言的特性;例如(宏定義)。
?
2. 左旋
對(duì)x進(jìn)行左旋,意味著"將x變成一個(gè)左節(jié)點(diǎn)"。
?
左旋的實(shí)現(xiàn)代碼(C++語(yǔ)言)
?
3. 右旋
對(duì)y進(jìn)行左旋,意味著"將y變成一個(gè)右節(jié)點(diǎn)"。
?
右旋的實(shí)現(xiàn)代碼(C++語(yǔ)言)
?
4. 添加
將一個(gè)節(jié)點(diǎn)插入到紅黑樹中,需要執(zhí)行哪些步驟呢?首先,將紅黑樹當(dāng)作一顆二叉查找樹,將節(jié)點(diǎn)插入;然后,將節(jié)點(diǎn)著色為紅色;最后,通過(guò)"旋轉(zhuǎn)和重新著色"等一系列操作來(lái)修正該樹,使之重新成為一顆紅黑樹。詳細(xì)描述如下:
第一步: 將紅黑樹當(dāng)作一顆二叉查找樹,將節(jié)點(diǎn)插入。
? ? ? ?紅黑樹本身就是一顆二叉查找樹,將節(jié)點(diǎn)插入后,該樹仍然是一顆二叉查找樹。也就意味著,樹的鍵值仍然是有序的。此外,無(wú)論是左旋還是右旋,若旋轉(zhuǎn)之前這棵樹是二叉查找樹,旋轉(zhuǎn)之后它一定還是二叉查找樹。這也就意味著,任何的旋轉(zhuǎn)和重新著色操作,都不會(huì)改變它仍然是一顆二叉查找樹的事實(shí)。
? ? ? 好吧?那接下來(lái),我們就來(lái)想方設(shè)法的旋轉(zhuǎn)以及重新著色,使這顆樹重新成為紅黑樹!
第二步:將插入的節(jié)點(diǎn)著色為"紅色"。
? ? ? 為什么著色成紅色,而不是黑色呢?為什么呢?在回答之前,我們需要重新溫習(xí)一下紅黑樹的特性:
(1) 每個(gè)節(jié)點(diǎn)或者是黑色,或者是紅色。
(2) 根節(jié)點(diǎn)是黑色。
(3) 每個(gè)葉子節(jié)點(diǎn)是黑色。 [注意:這里葉子節(jié)點(diǎn),是指為空的葉子節(jié)點(diǎn)!]
(4) 如果一個(gè)節(jié)點(diǎn)是紅色的,則它的子節(jié)點(diǎn)必須是黑色的。
(5) 從一個(gè)節(jié)點(diǎn)到該節(jié)點(diǎn)的子孫節(jié)點(diǎn)的所有路徑上包含相同數(shù)目的黑節(jié)點(diǎn)。
? ? 將插入的節(jié)點(diǎn)著色為紅色,不會(huì)違背"特性(5)"!少違背一條特性,就意味著我們需要處理的情況越少。接下來(lái),就要努力的讓這棵樹滿足其它性質(zhì)即可;滿足了的話,它就又是一顆紅黑樹了。o(∩∩)o...哈哈
第三步: 通過(guò)一系列的旋轉(zhuǎn)或著色等操作,使之重新成為一顆紅黑樹。
? ? ? ?第二步中,將插入節(jié)點(diǎn)著色為"紅色"之后,不會(huì)違背"特性(5)"。那它到底會(huì)違背哪些特性呢?
? ? ? ?對(duì)于"特性(1)",顯然不會(huì)違背了。因?yàn)槲覀円呀?jīng)將它涂成紅色了。
? ? ? ?對(duì)于"特性(2)",顯然也不會(huì)違背。在第一步中,我們是將紅黑樹當(dāng)作二叉查找樹,然后執(zhí)行的插入操作。而根據(jù)二叉查找數(shù)的特點(diǎn),插入操作不會(huì)改變根節(jié)點(diǎn)。所以,根節(jié)點(diǎn)仍然是黑色。
? ? ? ?對(duì)于"特性(3)",顯然不會(huì)違背了。這里的葉子節(jié)點(diǎn)是指的空葉子節(jié)點(diǎn),插入非空節(jié)點(diǎn)并不會(huì)對(duì)它們?cè)斐捎绊憽?/span>
? ? ? ?對(duì)于"特性(4)",是有可能違背的!
? ? ? 那接下來(lái),想辦法使之"滿足特性(4)",就可以將樹重新構(gòu)造成紅黑樹了。
添加操作的實(shí)現(xiàn)代碼(C++語(yǔ)言)
內(nèi)部接口?-- insert(root, node)的作用是將"node"節(jié)點(diǎn)插入到紅黑樹中。其中,root是根,node是被插入節(jié)點(diǎn)。
外部接口?-- insert(key)的作用是將"key"添加到紅黑樹中。
添加修正操作的實(shí)現(xiàn)代碼(C++語(yǔ)言)
insertFixUp(root, node)的作用是對(duì)應(yīng)"上面所講的第三步"。它是一個(gè)內(nèi)部接口。
?
5. 刪除操作
將紅黑樹內(nèi)的某一個(gè)節(jié)點(diǎn)刪除。需要執(zhí)行的操作依次是:首先,將紅黑樹當(dāng)作一顆二叉查找樹,將該節(jié)點(diǎn)從二叉查找樹中刪除;然后,通過(guò)"旋轉(zhuǎn)和重新著色"等一系列來(lái)修正該樹,使之重新成為一棵紅黑樹。詳細(xì)描述如下:
第一步:將紅黑樹當(dāng)作一顆二叉查找樹,將節(jié)點(diǎn)刪除。
? ? ? 這和"刪除常規(guī)二叉查找樹中刪除節(jié)點(diǎn)的方法是一樣的"。分3種情況:
① 被刪除節(jié)點(diǎn)沒(méi)有兒子,即為葉節(jié)點(diǎn)。那么,直接將該節(jié)點(diǎn)刪除就OK了。
② 被刪除節(jié)點(diǎn)只有一個(gè)兒子。那么,直接刪除該節(jié)點(diǎn),并用該節(jié)點(diǎn)的唯一子節(jié)點(diǎn)頂替它的位置。
③ 被刪除節(jié)點(diǎn)有兩個(gè)兒子。那么,先找出它的后繼節(jié)點(diǎn);然后把“它的后繼節(jié)點(diǎn)的內(nèi)容”復(fù)制給“該節(jié)點(diǎn)的內(nèi)容”;之后,刪除“它的后繼節(jié)點(diǎn)”。在這里,后繼節(jié)點(diǎn)相當(dāng)于替身,在將后繼節(jié)點(diǎn)的內(nèi)容復(fù)制給"被刪除節(jié)點(diǎn)"之后,再將后繼節(jié)點(diǎn)刪除。這樣就巧妙的將問(wèn)題轉(zhuǎn)換為"刪除后繼節(jié)點(diǎn)"的情況了,下面就考慮后繼節(jié)點(diǎn)。 在"被刪除節(jié)點(diǎn)"有兩個(gè)非空子節(jié)點(diǎn)的情況下,它的后繼節(jié)點(diǎn)不可能是雙子非空。既然"的后繼節(jié)點(diǎn)"不可能雙子都非空,就意味著"該節(jié)點(diǎn)的后繼節(jié)點(diǎn)"要么沒(méi)有兒子,要么只有一個(gè)兒子。若沒(méi)有兒子,則按"情況① "進(jìn)行處理;若只有一個(gè)兒子,則按"情況② "進(jìn)行處理。
第二步:通過(guò)"旋轉(zhuǎn)和重新著色"等一系列來(lái)修正該樹,使之重新成為一棵紅黑樹。
? ? ? ?因?yàn)?#34;第一步"中刪除節(jié)點(diǎn)之后,可能會(huì)違背紅黑樹的特性。所以需要通過(guò)"旋轉(zhuǎn)和重新著色"來(lái)修正該樹,使之重新成為一棵紅黑樹。
刪除操作的實(shí)現(xiàn)代碼(C++語(yǔ)言)
內(nèi)部接口?-- remove(root, node)的作用是將"node"節(jié)點(diǎn)插入到紅黑樹中。其中,root是根,node是被插入節(jié)點(diǎn)。
外部接口?-- remove(key)刪除紅黑樹中鍵值為key的節(jié)點(diǎn)。
刪除修正操作的實(shí)現(xiàn)代碼(C++語(yǔ)言)
removeFixup(root, node, parent)是對(duì)應(yīng)"上面所講的第三步"。它是一個(gè)內(nèi)部接口。
?
紅黑樹的C++實(shí)現(xiàn)(完整源碼)
下面是紅黑樹實(shí)現(xiàn)的完整代碼和相應(yīng)的測(cè)試程序。
(1) 除了上面所說(shuō)的"左旋"、"右旋"、"添加"、"刪除"等基本操作之后,還實(shí)現(xiàn)了"遍歷"、"查找"、"打印"、"最小值"、"最大值"、"創(chuàng)建"、"銷毀"等接口。
(2) 函數(shù)接口大多分為內(nèi)部接口和外部接口。內(nèi)部接口是private函數(shù),外部接口則是public函數(shù)。
(3) 測(cè)試代碼中提供了"插入"和"刪除"動(dòng)作的檢測(cè)開(kāi)關(guān)。默認(rèn)是關(guān)閉的,打開(kāi)方法可以參考"代碼中的說(shuō)明"。建議在打開(kāi)開(kāi)關(guān)后,在草稿上自己動(dòng)手繪制一下紅黑樹。
紅黑樹的實(shí)現(xiàn)文件(RBTree.h)
?View Code 紅黑樹的測(cè)試文件(RBTreeTest.cpp)
?
紅黑樹的C++測(cè)試程序
測(cè)試程序已經(jīng)包含在相應(yīng)的實(shí)現(xiàn)文件(MaxHeap.cpp)中了,這里就不再重復(fù)說(shuō)明。下面是測(cè)試程序的運(yùn)行結(jié)果:
總結(jié)
以上是生活随笔為你收集整理的红黑树(三)之 C++的实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 红黑树(二)之 C语言的实现
- 下一篇: AVL树(二)之 C++的实现