紅黑樹概念及其相關操作的實現

紅黑樹的概念

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

紅黑樹的性質

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

問題

為什么滿足上面的性質,紅黑樹就能保證:其最長路徑中節點個數不會超過最短路徑節點個數的兩 倍?
在這里插入圖片描述
極端情況下,剛好是兩倍,但是這種極端情況不存在。因為根結點是黑的,那么第二層的兩個結點都必須是紅色的。

紅黑樹的結構

在這里插入圖片描述

紅黑樹結點的定義

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;
};
為什么要將結點的默認顏色給成紅色的?

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

紅黑樹的插入

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

1. 按照二叉搜索的樹規則插入新節點

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

2. 檢測新節點插入后,紅黑樹的性質是否造到破壞

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

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

解決方式:將p,u改為黑,g改為紅,然后把g當成cur,繼續向上調整。
在這里插入圖片描述

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

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

在這里插入圖片描述

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

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

在這里插入圖片描述

紅黑樹的驗證

紅黑樹的檢測分為兩步:

  1. 檢測其是否滿足二叉搜索樹(中序遍歷是否為有序序列)
  2. 檢測其是否滿足紅黑樹的性質
template<class T>
class RBTree
{typedef RBTreeNode<T> Node;
public:RBTree(){_pHead = new Node;_pHead->_pLeft = _pHead;_pHead->_pRight = _pHead;//構造里已經將雙親給成空}bool Insert(const T&data){Node*& pRoot = GetRoot(); //必須以引用的方式進行接受if (nullptr == pRoot) //樹為空,創建根結點{pRoot = new Node(data,BLACK);pRoot->_pParent = _pHead;//只有一個結點,head就是根節點的雙親_pHead->_pLeft = pRoot; //改變左右指針域_pHead->_pRight = pRoot;return true;}else{//說明樹已經不為空了//1.按照二叉搜索樹的性質找到帶插入結點在紅黑樹的位置Node* pCur = pRoot;Node* pParent = nullptr;while (pCur){pParent = pCur;//標記雙親位置if (data < pCur->_data)pCur = pCur->_pLeft;else if (data > pCur->_data)pCur = pCur->_pRight;else//相同不插入return false;}//2. 插入新結點pCur = new Node(data);if (data < pParent->_data)pParent->_pLeft = pCur;elsepParent->_pRight = pCur;//3. 更新雙親位置pCur->_pParent = pParent;//以上沒錯//4.檢測:是否新結點插入后連在一起的紅色結點while (pParent!=_pHead && RED == pParent->_color){Node* granderFather = pParent->_pParent;if (pParent == granderFather->_pLeft){//叔叔結點在右側Node* uncle = granderFather->_pRight;//情況一:叔叔結點存在,且為紅if (uncle && RED == uncle->_color){pParent->_color = BLACK;uncle->_color = BLACK;granderFather->_color = RED;pCur = granderFather;pParent = pCur->_pParent;}//以上沒問題else{//情況三if (pCur == pParent->_pRight) //情況三{//轉變成情況二RotateLeft(pParent);swap(pParent, pCur);}//情況二pParent->_color = BLACK;granderFather->_color = RED;RotateRight(granderFather);}//以上沒問題}else{//叔叔結點在左側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.調整頭結點的左右指針域//保證根節點是黑色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 << "違反性質2:根結點顏色必須為黑色" << endl;return false;}//獲取一條路徑中結點的個數size_t blackCount = 0; //基準值Node* pCur = pRoot;while (pCur){if (pCur->_color == BLACK)blackCount++;pCur = pCur->_pLeft;}size_t pathBlack = 0; //每條路徑中的黑色結點個數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++;//檢測性質3Node* pParent = pRoot->_pParent;if (pParent != _pHead && pParent->_color == RED&&pRoot->_color == RED){cout << "違反性質3:不能有連在一起的紅色結點" << endl;return false;}if (nullptr == pRoot->_pLeft&&nullptr == pRoot->_pRight){//一條路徑到葉子if (blackCount != pathBlack){cout << "違反了性質4:每條路徑中黑色結點個數必須相同" << endl;return false;}}return _IsValidRBTree(pRoot->_pLeft, blackCount, pathBlack) &&_IsValidRBTree(pRoot->_pRight, blackCount, pathBlack);}Node* LeftMost(){//得到根節點Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最左側結點while (pCur->_pLeft)pCur = pCur->_pLeft;return pCur;}Node* RightMost(){//得到根節點Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最右側結點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是根結點if (pPParent == _pHead)GetRoot() = pSubL;else{//非根結點if (pPParent->_pLeft == pParent)pPParent->_pLeft = pSubL;elsepPParent->_pRight = pSubL;}}Node*& GetRoot()  //head是new出來的,head存在parent一定存在,按引用方式返回沒有問題{//得到根節點,也就是頭結點的下一個結點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樹都是高效的平衡二叉樹,增刪改查的時間復雜度都是O( log2N),紅黑樹不追求絕對平衡,其只需保證最長路徑不超過最短路徑的2倍,相對而言,降低了插入和旋轉的次數,所以在經常進行增刪的結構中性能比AVL樹更優,而且紅黑樹實現比較簡單,所以實際運用中紅黑樹更多。

紅黑樹的應用

  1. Java中的java.util.TreeMap,java.util.TreeSet
  2. C++ STL中的:map,multimap,multiset
  3. .NET中的:SortedDictionary,SortedSet 等

模擬實現map和set

模擬實現

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383123.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383123.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383123.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

模擬實現STL中map和set容器

紅黑樹的迭代器 //紅黑樹的迭代器 template<class T> struct RBTreeIterator {typedef RBTreeNode<T>Node;typedef RBTreeIterator<T> Self; public:RBTreeIterator(Node* pNode nullptr):_pNode(pNode){}//具有指針操作T& operator*(){return _pNode-…

【劍指offer】_05 連續子數組最大和

題目描述 HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會后,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全為正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,并期望旁邊的正數會彌補它呢&#…

排序上---(排序概念,常見排序算法,直接插入,希爾排序,直接選擇排序,堆排序)

排序的概念 排序&#xff1a;所謂排序&#xff0c;就是使一串記錄&#xff0c;按照其中的某個或某些關鍵字的大小&#xff0c;遞增或遞減的排列起來的操作。穩定性&#xff1a;假定在待排序的記錄序列中&#xff0c;存在多個具有相同的關鍵字的記錄&#xff0c;若經過排序&…

排序下---(冒泡排序,快速排序,快速排序優化,快速排序非遞歸,歸并排序,計數排序)

排序上 排序上 交換類排序 基本思想&#xff1a;所謂交換&#xff0c;就是根據序列中兩個記錄鍵值的比較結果來對換這兩個記錄在序列中的位置&#xff0c;交換排序的特點是&#xff1a;將鍵值較大的記錄向序列的尾部移動&#xff0c;鍵值較小的記錄向序列的前部移動。 冒泡…

哈希的概念及其操作

哈希概念 順序結構以及平衡樹中&#xff0c;元素關鍵碼與其存儲位置之間沒有對應的關系&#xff0c;因此在查找一個元素時&#xff0c;必須要經過關鍵碼的多次比較。順序查找時間復雜度為O(N)&#xff0c;平衡樹中為樹的高度&#xff0c;即O( Log2N)&#xff0c;搜索的效率取決…

軟件工程---1.概述

軟件的特征 抽象&#xff1a; 不可觸摸&#xff0c;邏輯實體&#xff0c;可記錄&#xff0c;但看不到復制成本低&#xff1a;不受物質材料的限制&#xff0c;不受物理定律或加工過程的制約&#xff0c;與開發成本相比&#xff0c;復制成本很低無折舊、受硬件制約、未完全擺脫手…

軟件工程---2.軟件過程

三個模型 瀑布模型增量模型集成和配置模型 沒有適用于所有不同類型軟件開發的過程模型。 瀑布模型 需求定義系統和軟件的設計實現與單元測試集成與系統測試運行與維護 瀑布模型的特征 從上一項活動中接受該項活動的工作成果&#xff08;工作產品&#xff09;&#xff0c;作…

軟件工程---3.敏捷軟件開發

敏捷軟件開發 極限編程&#xff08;XP&#xff0c; Beck1999&#xff09;Scrum方法&#xff08;Schwaber and Beedle 2001&#xff09;DSDM方法&#xff08;Stapleton 2003&#xff09; 敏捷軟件的開發宣言 個體和交互勝過過程和工具可以工作的軟件勝過面面俱到的文檔客戶合…

軟件工程---4.需求工程

需求工程定義 找出、分析、文檔化并且檢查需求的過程被稱為需求工程 需求的兩個描述層次 用戶需求&#xff0c;指高層的抽象需求。使用自然語言、圖形描述需求。系統需求&#xff0c;指底層的詳細需求。使用系統需求文檔&#xff08;有時被稱為功能規格說明&#xff09;應該…

軟件工程---5.系統建模

從不同視角對系統建模 外部視角&#xff0c;上下文模型&#xff0c;對系統上下文或環境建模交互視角&#xff0c;交互模型&#xff08;功能模型&#xff09;&#xff0c;對系統與參與者或系統內構件之間的交互建模結構視角&#xff0c;結構模型&#xff08;靜態模型&#xff0…

軟件工程---6.體系結構設計

體系結構模型是什么&#xff1f; 體系結構模型&#xff0c;該模型描述系統如何被組織為一組相互通信的構件 體系結構分類 小體系結構關注單個程序的體系結構。在這個層次上&#xff0c;我們關注單個的程序是如何補分解為構件的。大體系結構關注包括其他系統、程序和程序構件…

【劍指offer】_06 變態跳臺階

題目描述 一只青蛙一次可以跳上1級臺階&#xff0c;也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。 解題思路 鏈接&#xff1a;https://www.nowcoder.com/questionTerminal/22243d016f6b47f2a6928b4313c85387 關于本題&#xff0c;前提是…

【劍指offer】_07 矩形覆蓋

題目描述 我們可以用21的小矩形橫著或者豎著去覆蓋更大的矩形。請問用n個21的小矩形無重疊地覆蓋一個2*n的大矩形&#xff0c;總共有多少種方法&#xff1f; 解題思路 依舊是斐波那契數列 2n的大矩形&#xff0c;和n個21的小矩形 其中target*2為大矩陣的大小 有以下幾種情形…

軟件工程---07.設計與實現

軟件設計和軟件實現 軟件設計是一個創造性的活動&#xff0c;在此活動中需要基于客戶需求識別軟件構件及其關系。軟件實現是將設計實現為一個程序的過程 為開發一個系統設計&#xff0c;你需要 理解并定義上下文模型以及系統的外部交互設計系統體系結構識別系統中的主要對象…

軟件工程---08.軟件測試

測試 測試的正向思維&#xff08;確認測試&#xff09; 向開發人員和客戶展示軟件滿足其需求測試的逆向思維&#xff08;缺陷測試&#xff09;找出可能導致軟件行為不正確原因。測試是更廣闊的軟件確認和驗證( Verification and Validation; V & V)過程的一部分。驗證和確…

軟件工程---15.軟件復用

復用的圖(牢記) 軟件復用的好處 開發加速有效的專家利用提高可依賴性降低開發成本降低過程風險符合標準 軟件復用的缺點 創建&#xff0c;維護以及使用一個構件庫查找&#xff0c;理解以及適配可復用構件維護成本增加缺少工具支持“不是在這里發明的”綜合癥 應用框架 現在…

軟件工程---16.基于構件的軟件工程

CBSE CBSE是定義、實現、集成或組裝松散耦合的獨立構件成為系統的過程。 基于構件的軟件工程的要素有: 完全由接口進行規格說明的獨立構件。構件標準使構件集成變得更為容易。中間件為構件集成提供軟件支持。開發過程適合基于構件的軟件工程。 CBSE的設計原則 構件是獨立的…

軟件工程---17.分布式軟件工程

分布式系統的5個優點 資源共享開放性并發性可伸縮性容錯性 分布式計算中必須考慮的設計問題 透明性&#xff1a;隱藏底層分布 開放性 可伸縮性 三個維度 規模&#xff1a;又分為增強擴展(單挑)&#xff0c;增加擴展(群毆)分布可靠性 信息安全性 主要防止以下類型的攻擊 攔…

軟件工程---18.面向服務的軟件工程

什么是Web服務 一個松耦合、可復用的軟件構件&#xff0c;封裝了離散的功能&#xff0c;該功能是分布式的并且可以被程序訪問。Web服務是通過標準互聯網和基于XML的協議被訪問的服務。 服務和軟件構件之間的一個重要的區別是 服務應該總是獨立的和松耦合的Web 服務沒有“請求…

【劍指offer】_08.數值的整數次方

題目描述 給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。 保證base和exponent不同時為0 解題思路 首先一個數的任意次方&#xff0c;這個數有可能是負數和正數和零&#xff0c;然后次方也有可能是負數和正數和零 當這個數是零時&#xff…