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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构拾遗(1) --红黑树的设计与实现(上)

發(fā)布時(shí)間:2025/3/17 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构拾遗(1) --红黑树的设计与实现(上) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

紅黑樹是一種自平衡的二叉查找樹,是在計(jì)算機(jī)科學(xué)中用到的一種數(shù)據(jù)結(jié)構(gòu),典型的用途是實(shí)現(xiàn)關(guān)聯(lián)數(shù)組(C++?STL?中的map/set)。它是在1972年由Rudolf?Bayer發(fā)明的,他稱之為"對(duì)稱二叉B樹",它現(xiàn)代的名字是在?Leo?J.?Guibas?和?Robert?Sedgewick?于1978年寫的一篇論文中獲得的。紅黑樹雖然很復(fù)雜,但它的操作有著良好的最壞情況運(yùn)行時(shí)間,并且在實(shí)踐中是高效的:?它可以在O(log?n)時(shí)間內(nèi)做查找,插入和刪除,這里的n?是樹中元素的數(shù)目(來源:百度百科)。

?

算法導(dǎo)論對(duì)R-B?Tree的介紹:

紅黑樹,一種二叉查找樹,但在每個(gè)結(jié)點(diǎn)上增加一個(gè)存儲(chǔ)位表示結(jié)點(diǎn)的顏色,可以是Red或Black。通過對(duì)任何一條從根到葉子的路徑上各個(gè)結(jié)點(diǎn)著色方式的限制,紅黑樹確保沒有一條路徑會(huì)比其他路徑長出兩倍,因而是接近平衡的

?

由于紅黑樹也是一顆二叉查找樹,?因此紅黑樹也滿足二叉查找樹的一般性質(zhì)(關(guān)于二叉查找樹的一般性質(zhì)請(qǐng)參考博客:http://blog.csdn.net/zjf280441589/article/details/42611161)

但由于普通的二叉查找樹不具備自動(dòng)平衡的功能,?因此普通的二叉查找樹樹很有可能會(huì)退化成為一條鏈表,?若二叉樹退化成了一棵具有n個(gè)結(jié)點(diǎn)的線性鏈后,則插入/刪除/查找操作最壞情況運(yùn)行時(shí)間就變?yōu)榱薕(n)。

因此就有了紅黑樹,?他的最終查找、插入、刪除的時(shí)間復(fù)雜度最壞情況下依然為O(logn),?而紅黑樹之所以這么牛的原因就是它在二叉查找樹的基礎(chǔ)上增加了著色和相關(guān)的性質(zhì),?這就是紅黑規(guī)則:


紅黑規(guī)則

? ?1.每一個(gè)節(jié)點(diǎn)不是紅色的就是黑色的;

? ?2.根總是黑色的;

? ?3.如果節(jié)點(diǎn)是紅色的,?則它的子節(jié)點(diǎn)必須是黑色的;

? ?4.從根到葉節(jié)點(diǎn)的每條路徑,?必須包含相同數(shù)目的黑色節(jié)點(diǎn)(黑高度相同);

? ?5.每個(gè)葉結(jié)點(diǎn)(葉結(jié)點(diǎn)即指樹尾端NIL指針或NULL結(jié)點(diǎn))是黑的;


? 根據(jù)規(guī)則4,?新增節(jié)點(diǎn)必須為紅;?

? 根據(jù)規(guī)則3,?新增節(jié)點(diǎn)之父節(jié)點(diǎn)必須為黑. ?

? 當(dāng)新節(jié)點(diǎn)根據(jù)二叉樹的規(guī)則到達(dá)插入點(diǎn),?卻未能符合上述紅黑規(guī)則時(shí),?就必須調(diào)整顏色并旋轉(zhuǎn)樹形;

?

紅黑結(jié)點(diǎn)

template <typename Type> class RedBlackNode {friend RedBlackTree<Type>; private:RedBlackNode(const Type &_element = Type(),RedBlackNode *_left = NULL,RedBlackNode *_right = NULL,int _color = RedBlackTree<Type>::BLACK): element(_element), left(_left), right(_right), color(_color) {}private: //為了在紅黑樹尚未完成的時(shí)候進(jìn)行測(cè)試 //故將之作為public, 待紅黑樹完成之時(shí), //就是將其改為private之時(shí); public:Type element; //節(jié)點(diǎn)值RedBlackNode *left;RedBlackNode *right;//紅黑樹的顏色int color; };

紅黑樹

template <typename Type> class RedBlackTree { public://為結(jié)點(diǎn)定義別名typedef RedBlackNode<Type> Node;enum {RED, BLACK};public://此時(shí)紅黑樹所支持的操作尚不完善,//而且函數(shù)功能(如析構(gòu)函數(shù), insert操作)//實(shí)現(xiàn)的也并不理想,//但隨著紅黑樹代碼的不斷演進(jìn),//這些代碼會(huì)不斷的完善RedBlackTree(const Type &negInf);~RedBlackTree();void insert(const Type &data);private: //為了在紅黑樹尚未完成的時(shí)候進(jìn)行測(cè)試 //故將之作為public, 待紅黑樹完成之時(shí), //就是將其改為private之時(shí); public://指向紅黑樹的頭(偽根節(jié)點(diǎn))//RedBlackNode<Type> *header;Node *header;//空節(jié)點(diǎn)Node *nullNode;//在插入過程中需要的指針Node *current; //當(dāng)前節(jié)點(diǎn)Node *parent; //父節(jié)點(diǎn)Node *grand; //祖父節(jié)點(diǎn)(爺爺)Node *great; //曾祖父節(jié)點(diǎn)(爺爺?shù)陌职?/* 單旋轉(zhuǎn) *///帶著左孩子旋轉(zhuǎn): 向右旋轉(zhuǎn)void rotateWithLeftChild(Node *& k2) const;//帶著有孩子旋轉(zhuǎn): 向左旋轉(zhuǎn)void rotateWithRightChild(Node *& k1) const;/* 雙旋轉(zhuǎn) *///向右轉(zhuǎn)void doubleRotateWithLeftChild(Node *& k3) const;//向左轉(zhuǎn)void doubleRotateWithRightChild(Node *& k1) const; };

構(gòu)造與析構(gòu)

//構(gòu)造函數(shù) template <typename Type> RedBlackTree<Type>::RedBlackTree(const Type &negInf) {nullNode = new Node();header = new Node(negInf, nullNode, nullNode); } //這一版的析構(gòu)函數(shù)實(shí)現(xiàn)并不完善,在后續(xù)的版本我們會(huì)繼續(xù)完善該析構(gòu)函數(shù) template <typename Type> RedBlackTree<Type>::~RedBlackTree() {delete nullNode;delete header; }

一個(gè)二叉查找樹的insert

//這時(shí)的insert其實(shí)就是一個(gè)普通的 //二叉查找樹的insert, 完全沒要照顧到 //二叉樹的平衡, 以及紅黑規(guī)則的實(shí)現(xiàn) template <typename Type> void RedBlackTree<Type>::insert(const Type &data) {great = grand = parent = current = header;//在此處令nullNode成為data, 以作哨兵nullNode->element = data;while (current->element != data){//讓祖父成為曾祖父, 父親成為祖父, 自己成為父親//每個(gè)人都長了一輩great = grand;grand = parent;parent = current;current = (data < current->element) ? current->left: current->right;}//如果樹中包含相同的元素if (current != nullNode)throw DuplicateItemException();current = new Node(data, nullNode, nullNode);if (data < parent->element)parent->left = current;elseparent->right = current;//在后續(xù)的版本上,需要加上自動(dòng)平衡(即實(shí)現(xiàn)紅黑規(guī)則) -> 紅黑樹 }

單旋轉(zhuǎn)

注意:內(nèi)側(cè)孫子節(jié)點(diǎn)橫向移動(dòng)(注意下圖結(jié)點(diǎn)37);

?

左(單)旋:

?

當(dāng)在某個(gè)結(jié)點(diǎn)k1上,做左旋操作時(shí),我們假設(shè)它的右孩子k2不是NIL[T](k1可以是任何不是NIL[T]的左孩子結(jié)點(diǎn));?左旋以k1到k2之間的鏈為“支軸”進(jìn)行,它使k2成為該子樹新的根,而k2的左孩子B則成為k1的右孩子。?

//實(shí)現(xiàn) //向左轉(zhuǎn) template <typename Type> void RedBlackTree<Type>::rotateWithRightChild(Node *& k1) const {Node *k2 = k1->right;//結(jié)點(diǎn)B橫向移動(dòng)k1->right = k2->left;//令k2提領(lǐng)k1k2->left = k1;//令k2為根(使k2替代k1的位置)k1 = k2; }

右(單)旋:

?

? ? 過程與左旋類似;

//實(shí)現(xiàn) //向右轉(zhuǎn) template <typename Type> void RedBlackTree<Type>::rotateWithLeftChild(Node *& k2) const {//首先將B橫向移動(dòng)Node *k1 = k2->left;k2->left = k1->right;//令k1提領(lǐng)k2k1->right = k2;//令k1為根(使k1替代k2的位置)k2 = k1; }

測(cè)試(在完成單旋轉(zhuǎn)之后):

?

? ? (構(gòu)造一顆二叉查找樹樹如圖所示,?左邊為尚未旋轉(zhuǎn)之前,?右為旋轉(zhuǎn)之后)

//測(cè)試代碼如下: int main() {//用NEG_INF來代表負(fù)無窮大const int NEG_INF = -999999;RedBlackTree<int> tree(NEG_INF);//單旋轉(zhuǎn)時(shí)候的測(cè)試數(shù)據(jù)tree.insert(30);tree.insert(15);tree.insert(70);tree.insert(20);cout << tree.header->right->element << endl;cout << tree.header->right->left->element << endl;cout << tree.header->right->right->element << endl;cout << tree.header->right->left->right->element << endl; //20//向右旋轉(zhuǎn)cout << "向右旋轉(zhuǎn)" << endl;tree.rotateWithLeftChild(tree.header->right);cout << tree.header->right->element << endl; //15cout << tree.header->right->right->element << endl; //30cout << tree.header->right->right->left->element << endl; //20cout << tree.header->right->right->right->element << endl; //70//然后再向左轉(zhuǎn)(又轉(zhuǎn)了回來)cout << "向左旋轉(zhuǎn)" << endl;tree.rotateWithRightChild(tree.header->right);cout << tree.header->right->element << endl;cout << tree.header->right->left->element << endl;cout << tree.header->right->right->element << endl;cout << tree.header->right->left->right->element << endl; //20 }

雙旋轉(zhuǎn)

單旋轉(zhuǎn)有時(shí)會(huì)出現(xiàn)一個(gè)問題(如下圖所示):


? ? (如果內(nèi)側(cè)子孫節(jié)點(diǎn)[k1]過深,?則將其單向移動(dòng)是不會(huì)解決問題的)

?

于是就有了雙旋轉(zhuǎn)

向右雙旋轉(zhuǎn):

? ?1.首先以k1為軸,?k1與k2向左旋轉(zhuǎn);

? ?2.然后以k3為軸,?k3與旋轉(zhuǎn)之后的k1向右旋轉(zhuǎn);

//實(shí)現(xiàn) //向右雙旋轉(zhuǎn) template <typename Type> void RedBlackTree<Type>::doubleRotateWithLeftChild(Node *& k3) const {//首先將其左兒子(k1)向左單旋轉(zhuǎn)rotateWithRightChild(k3->left);//然后將自己(k3)向右單旋轉(zhuǎn)rotateWithLeftChild(k3); }

向左雙旋轉(zhuǎn):

? ?1.首先以k3為軸,?k2與k3向右旋轉(zhuǎn);

? ?2.然后以k1為軸,?k1與旋轉(zhuǎn)之后的k2向左旋轉(zhuǎn);

//實(shí)現(xiàn) //向左雙旋轉(zhuǎn) template <typename Type> void RedBlackTree<Type>::doubleRotateWithRightChild(Node *& k1) const {//首先將其右兒子(k2)向右單旋轉(zhuǎn)rotateWithLeftChild(k1->right);//然后將自己(k1)向左單旋轉(zhuǎn)rotateWithRightChild(k1); } //注:其實(shí)紅黑樹中并沒有用到雙旋轉(zhuǎn), 而是自己實(shí)現(xiàn)了一個(gè)rotate操作, //在此只為了學(xué)習(xí)雙旋轉(zhuǎn)的理論;


測(cè)試(在完成雙旋轉(zhuǎn)之后):


(構(gòu)造一顆二叉查找樹樹如圖所示,?左邊為尚未旋轉(zhuǎn)之前,?右為旋轉(zhuǎn)之后,?以8為軸進(jìn)行雙旋轉(zhuǎn))


int main() {//用NEG_INF來代表負(fù)無窮大const int NEG_INF = -999999;RedBlackTree<int> tree(NEG_INF);//雙旋轉(zhuǎn)時(shí)的測(cè)試數(shù)據(jù)tree.insert(12);tree.insert(16);tree.insert(8);tree.insert(10);tree.insert(4);tree.insert(14);tree.insert(2);tree.insert(6);tree.insert(5);cout << tree.header->right->element << endl; //12cout << tree.header->right->left->element << endl; //8cout << tree.header->right->right->element << endl; //16cout << tree.header->right->left->left->element << endl; //4cout << tree.header->right->left->right->element << endl; //10cout << tree.header->right->right->left->element << endl; //14 // cout << tree.header->right->left->right->left->element << endl; //5曾經(jīng)做過哨兵 // cout << tree.header->right->left->right->right->element << endl; //5cout << tree.header->right->left->left->left->element << endl; //2cout << tree.header->right->left->left->right->element << endl; //6cout << tree.header->right->left->left->right->left->element << endl; //5cout << "\n向右雙旋轉(zhuǎn)" << endl;//以8為基準(zhǔn)向右雙旋轉(zhuǎn)tree.doubleRotateWithLeftChild(tree.header->right->left);cout << tree.header->right->element << endl; //12cout << tree.header->right->left->element << endl; //6cout << tree.header->right->right->element << endl; //16cout << tree.header->right->left->left->element << endl; //4cout << tree.header->right->left->right->element << endl; //8cout << tree.header->right->right->left->element << endl; //14cout << tree.header->right->left->left->left->element << endl; //2cout << tree.header->right->left->left->right->element << endl; //5cout << tree.header->right->left->right->right->element << endl; //10 }

實(shí)現(xiàn)紅黑樹用到的異常

class DSException { public:DSException(const string &_message = string()): message(_message) {}~DSException() {}virtual string what() const{return message;}virtual string toString() const{return "Exception: " + what();}private:std::string message; };class DuplicateItemException : public DSException { public:DuplicateItemException(const string &_msg = string()): DSException(_msg) {} };


總結(jié)

以上是生活随笔為你收集整理的数据结构拾遗(1) --红黑树的设计与实现(上)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。