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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

红黑树概念及其相关操作的实现

發(fā)布時間:2023/11/30 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 红黑树概念及其相关操作的实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

紅黑樹的概念

紅黑樹,是一種二叉搜索樹,但它并不像AVL樹一樣,每個結(jié)點綁定一個平衡因子。但在每個結(jié)點上增加一個存儲位表示結(jié)點的顏色,可以是Red或Black。 通過 對任何一條從根到葉子的路徑上各個結(jié)點著色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,因而是接近平衡的。
最長路徑中結(jié)點的個數(shù)不會超過最短路徑中結(jié)點個數(shù)的兩倍

紅黑樹的性質(zhì)

  • 每個結(jié)點不是紅色就是黑色
  • 根節(jié)點是黑色的 ,空樹也是紅黑樹
  • 如果一個節(jié)點是紅色的,則它的兩個孩子結(jié)點是黑色的 ,不可能出現(xiàn)連在一起的紅色結(jié)點
  • 對于每個結(jié)點,從該結(jié)點到其所有后代葉結(jié)點的簡單路徑上,均包含相同數(shù)目的黑色結(jié)點 ,每條路徑里黑色結(jié)點個數(shù)是相等的
  • 每個葉子結(jié)點都是黑色的(此處的葉子結(jié)點指的是空結(jié)點)
  • 問題

    為什么滿足上面的性質(zhì),紅黑樹就能保證:其最長路徑中節(jié)點個數(shù)不會超過最短路徑節(jié)點個數(shù)的兩 倍?

    極端情況下,剛好是兩倍,但是這種極端情況不存在。因為根結(jié)點是黑的,那么第二層的兩個結(jié)點都必須是紅色的。

    紅黑樹的結(jié)構(gòu)

    紅黑樹結(jié)點的定義

    enum COLOR{RED,BLACK};template<class T> struct RBTreeNode {RBTreeNode(const T& data = T(), COLOR color = RED):_pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _color(color) {}RBTreeNode<T>* _pLeft;RBTreeNode<T>* _pRight;RBTreeNode<T>* _pParent;T _data;COLOR _color; };
    為什么要將結(jié)點的默認(rèn)顏色給成紅色的?

    因為如果默認(rèn)顏色是黑色的話,往已經(jīng)是一顆紅黑樹里插入的話,性質(zhì)4一定遭到破壞,所以給成紅色,有些情況下破壞,有些情況下不被破壞

    紅黑樹的插入

    紅黑樹是在二叉搜索樹的基礎(chǔ)上加上其平衡限制條件,因此紅黑樹的插入可分為兩步:

    1. 按照二叉搜索的樹規(guī)則插入新節(jié)點

    template<class T> class RBTree {typedef RBTreeNode<T> Node; public:RBTree(){_pHead = new Node;_pHead->_pLeft = _pHead;_pHead->_pRight = _pHead;//構(gòu)造里已經(jīng)將雙親給成空}bool Insert(const T&data){Node* pRoot = GetRoot();if (nullptr == pRoot) //樹為空,創(chuàng)建根結(jié)點{pRoot = new Node(data,BLACK);pRoot->_pParent = _pHead;//只有一個結(jié)點,head就是根節(jié)點的雙親_pHead->_pLeft = pRoot; //改變左右指針域_pHead->_pRight = pRoot;return true;}else{//說明樹已經(jīng)不為空了//1.按照二叉搜索樹的性質(zhì)找到帶插入結(jié)點在紅黑樹的位置Node* pCur = pRoot;Node* pParent = nullptr;while (pCur){pParent = pCur;//標(biāo)記雙親位置if (data < pCur->_data)pCur = pCur->_pLeft;else if (data>pCur->_data)pCur = pCur->_pRight;else//相同不插入return false;}//2. 插入新結(jié)點pCur = new Node(data);if (data < pParent->_data)pParent->_pLeft = pCur;else{pParent->_pRight = pCur;}//3. 更新雙親位置pCur->_pParent = pParent;//4.檢測:是否新結(jié)點插入后連在一起的紅色結(jié)點if (RED == pParent->_color){//調(diào)整//檢測新節(jié)點插入后,紅黑樹的性質(zhì)是否造到破壞}}//5.調(diào)整頭結(jié)點的左右指針域//保證根節(jié)點是黑色pRoot->_color = BLACK;_pHead->_pLeft=leftMost();_pHead->_pRight = RightMost();return true;}Node* LeftMost(){//得到根節(jié)點Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最左側(cè)結(jié)點while (pCur->_pLeft)pCur = pCur->_pLeft;return pCur;}Node* RightMost(){//得到根節(jié)點Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最右側(cè)結(jié)點while (pCur->_pRight)pCur = pCur->_pRight;return pCur;} protected:Node*& GetRoot() //head是new出來的,head存在parent一定存在,按引用方式返回沒有問題{//得到根節(jié)點,也就是頭結(jié)點的下一個結(jié)點return _pHead->_pParent;} private:Node* _pHead; };

    2. 檢測新節(jié)點插入后,紅黑樹的性質(zhì)是否造到破壞

    因為新節(jié)點的默認(rèn)顏色是紅色,因此:如果其雙親節(jié)點的顏色是黑色,沒有違反紅黑樹任何性質(zhì),則不需要調(diào)整;但當(dāng)新插入節(jié)點的雙親節(jié)點顏色為紅色時,就違反了性質(zhì)三不能有連在一起的紅色節(jié)點此時需要對紅黑樹分情況來討論
    cur為當(dāng)前節(jié)點,p為父節(jié)點,g為祖父節(jié)點,u為叔叔節(jié)點

    2.1 cur為紅,p為紅,g為黑,u存在且為紅

    解決方式:將p,u改為黑,g改為紅,然后把g當(dāng)成cur,繼續(xù)向上調(diào)整。

    2.2 cur為紅,p為紅,g為黑,u不存在/u為黑

    p為g的左孩子,cur為p的左孩子,則進行右單旋轉(zhuǎn);相反,
    p為g的右孩子,cur為p的右孩子,則進行左單旋轉(zhuǎn)
    p、g變色–p變黑,g變紅

    • 如果u不存在,假設(shè)cur本來就存在,cur和雙親比然是黑的,因為兩個紅的不能連在一起,那么這條路徑里就有兩個黑色,所以不滿足性質(zhì)4,cur所以一定是新插入的結(jié)點
    • 如果u存在且為黑色,右側(cè)路徑里有兩個黑色路徑,因為兩條路徑黑色結(jié)點必須一樣。新節(jié)點一插入,插入到cur的子樹中,導(dǎo)致子樹中不滿足情況。向上調(diào)整時,把cur改成黑色。

    2.3 cur為紅,p為紅,g為黑,u不存在/u為黑

    紅黑樹的驗證

    紅黑樹的檢測分為兩步:

  • 檢測其是否滿足二叉搜索樹(中序遍歷是否為有序序列)
  • 檢測其是否滿足紅黑樹的性質(zhì)
  • template<class T> class RBTree {typedef RBTreeNode<T> Node; public:RBTree(){_pHead = new Node;_pHead->_pLeft = _pHead;_pHead->_pRight = _pHead;//構(gòu)造里已經(jīng)將雙親給成空}bool Insert(const T&data){Node*& pRoot = GetRoot(); //必須以引用的方式進行接受if (nullptr == pRoot) //樹為空,創(chuàng)建根結(jié)點{pRoot = new Node(data,BLACK);pRoot->_pParent = _pHead;//只有一個結(jié)點,head就是根節(jié)點的雙親_pHead->_pLeft = pRoot; //改變左右指針域_pHead->_pRight = pRoot;return true;}else{//說明樹已經(jīng)不為空了//1.按照二叉搜索樹的性質(zhì)找到帶插入結(jié)點在紅黑樹的位置Node* pCur = pRoot;Node* pParent = nullptr;while (pCur){pParent = pCur;//標(biāo)記雙親位置if (data < pCur->_data)pCur = pCur->_pLeft;else if (data > pCur->_data)pCur = pCur->_pRight;else//相同不插入return false;}//2. 插入新結(jié)點pCur = new Node(data);if (data < pParent->_data)pParent->_pLeft = pCur;elsepParent->_pRight = pCur;//3. 更新雙親位置pCur->_pParent = pParent;//以上沒錯//4.檢測:是否新結(jié)點插入后連在一起的紅色結(jié)點while (pParent!=_pHead && RED == pParent->_color){Node* granderFather = pParent->_pParent;if (pParent == granderFather->_pLeft){//叔叔結(jié)點在右側(cè)Node* uncle = granderFather->_pRight;//情況一:叔叔結(jié)點存在,且為紅if (uncle && RED == uncle->_color){pParent->_color = BLACK;uncle->_color = BLACK;granderFather->_color = RED;pCur = granderFather;pParent = pCur->_pParent;}//以上沒問題else{//情況三if (pCur == pParent->_pRight) //情況三{//轉(zhuǎn)變成情況二RotateLeft(pParent);swap(pParent, pCur);}//情況二pParent->_color = BLACK;granderFather->_color = RED;RotateRight(granderFather);}//以上沒問題}else{//叔叔結(jié)點在左側(cè)Node* uncle = granderFather->_pLeft;//情況一的反情況if (uncle && uncle->_color == RED){pParent->_color = BLACK;uncle->_color = BLACK;granderFather->_color = RED;pCur = granderFather;pParent = pCur->_pParent;}//以上沒問題else{//情況三的反情況if (pCur == pParent->_pLeft) /**/{//情況三的反情況變成情況二的反情況RotateRight(pParent);swap(pParent, pCur);}//情況二反情況處理pParent->_color = BLACK;granderFather->_color = RED;RotateLeft(granderFather);}//以上沒問題}}}//5.調(diào)整頭結(jié)點的左右指針域//保證根節(jié)點是黑色pRoot->_color = BLACK;_pHead->_pLeft = LeftMost();_pHead->_pRight = RightMost();return true;}void InOrder(){_InOrder(GetRoot());}//檢測紅黑樹bool IsValidRBTree(){Node* pRoot = GetRoot();if (nullptr == pRoot)return true;if (pRoot->_color != BLACK){cout << "違反性質(zhì)2:根結(jié)點顏色必須為黑色" << endl;return false;}//獲取一條路徑中結(jié)點的個數(shù)size_t blackCount = 0; //基準(zhǔn)值Node* pCur = pRoot;while (pCur){if (pCur->_color == BLACK)blackCount++;pCur = pCur->_pLeft;}size_t pathBlack = 0; //每條路徑中的黑色結(jié)點個數(shù)return _IsValidRBTree(pRoot, blackCount,pathBlack);} protected:bool _IsValidRBTree(Node* pRoot, size_t blackCount, size_t pathBlack){if (nullptr == pRoot)return true;if (pRoot->_color == BLACK)pathBlack++;//檢測性質(zhì)3Node* pParent = pRoot->_pParent;if (pParent != _pHead && pParent->_color == RED&&pRoot->_color == RED){cout << "違反性質(zhì)3:不能有連在一起的紅色結(jié)點" << endl;return false;}if (nullptr == pRoot->_pLeft&&nullptr == pRoot->_pRight){//一條路徑到葉子if (blackCount != pathBlack){cout << "違反了性質(zhì)4:每條路徑中黑色結(jié)點個數(shù)必須相同" << endl;return false;}}return _IsValidRBTree(pRoot->_pLeft, blackCount, pathBlack) &&_IsValidRBTree(pRoot->_pRight, blackCount, pathBlack);}Node* LeftMost(){//得到根節(jié)點Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最左側(cè)結(jié)點while (pCur->_pLeft)pCur = pCur->_pLeft;return pCur;}Node* RightMost(){//得到根節(jié)點Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最右側(cè)結(jié)點while (pCur->_pRight)pCur = pCur->_pRight;return pCur;}void RotateLeft(Node* pParent){Node* pSubR = pParent->_pRight;Node* pSubRL = pSubR->_pLeft;pParent->_pRight = pSubRL;if (pSubRL)pSubRL->_pParent = pParent;pSubR->_pLeft = pParent;Node* pPParent = pParent->_pParent;pParent->_pParent = pSubR;pSubR->_pParent = pPParent;if (pPParent == _pHead)GetRoot() = pSubR;else{if (pPParent->_pLeft == pParent)pPParent->_pLeft = pSubR;elsepPParent->_pRight = pSubR;}}void RotateRight(Node* pParent){Node* pSubL = pParent->_pLeft;Node* pSubLR = pSubL->_pRight;pParent->_pLeft = pSubLR;if (pSubLR)pSubLR->_pParent = pParent;pSubL->_pRight = pParent;Node* pPParent = pParent->_pParent;pParent->_pParent = pSubL;pSubL->_pParent = pPParent;//pParent是根結(jié)點if (pPParent == _pHead)GetRoot() = pSubL;else{//非根結(jié)點if (pPParent->_pLeft == pParent)pPParent->_pLeft = pSubL;elsepPParent->_pRight = pSubL;}}Node*& GetRoot() //head是new出來的,head存在parent一定存在,按引用方式返回沒有問題{//得到根節(jié)點,也就是頭結(jié)點的下一個結(jié)點return _pHead->_pParent;}void _InOrder(Node* pRoot){if (pRoot){_InOrder(pRoot->_pLeft);cout << pRoot->_data << " ";_InOrder(pRoot->_pRight);}} private:Node* _pHead; };void TestRBTree() {int array[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };RBTree<int>t;for (auto e : array)t.Insert(e);t.InOrder();if (t.IsValidRBTree()){cout << "t is vaild rbTree" << endl;}elsecout << "t is not vaild rbTree" << endl; }

    紅黑樹的刪除

    參考鏈接1
    參考鏈接2

    紅黑樹與AVL樹的比較

    紅黑樹和AVL樹都是高效的平衡二叉樹,增刪改查的時間復(fù)雜度都是O( log2N),紅黑樹不追求絕對平衡,其只需保證最長路徑不超過最短路徑的2倍,相對而言,降低了插入和旋轉(zhuǎn)的次數(shù),所以在經(jīng)常進行增刪的結(jié)構(gòu)中性能比AVL樹更優(yōu),而且紅黑樹實現(xiàn)比較簡單,所以實際運用中紅黑樹更多。

    紅黑樹的應(yīng)用

  • Java中的java.util.TreeMap,java.util.TreeSet
  • C++ STL中的:map,multimap,multiset
  • .NET中的:SortedDictionary,SortedSet 等
  • 模擬實現(xiàn)map和set

    模擬實現(xiàn)

    總結(jié)

    以上是生活随笔為你收集整理的红黑树概念及其相关操作的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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