[數據結構]紅黑樹,詳細圖解插入

目錄

一、紅黑樹的概念

二、紅黑樹的性質

三、紅黑樹節點的定義

四、紅黑樹的插入(步驟)

1.為什么新插入的節點必須給紅色?

2、插入紅色節點后,判定紅黑樹性質是否被破壞

五、插入出現連續紅節點情況分析+圖解(看uncle節點)

5.1、uncle存在且為紅

5.2、uncle不存在

1、單旋

2、雙旋

5.3、uncle存在且為黑

1、單旋

2、雙旋

六、插入總結

1、紅黑樹插入的兩種步驟

?2、插入代碼

七、紅黑樹總結及代碼


一、紅黑樹的概念

紅黑樹,是一種二叉搜索樹,但在每個結點上增加一個存儲位表示結點的顏色,可以是Red或 Black。 通過對任何一條從根到葉子的路徑上各個結點著色方式的限制

紅黑樹確保——沒有一條路徑會比其他路徑長出兩倍,因而是接近平衡的


二、紅黑樹的性質

1. 每個結點不是紅色就是黑色

2. 根節點是黑色的?

3. 如果一個節點是紅色的,則它的兩個孩子結點是黑色的 (沒有連續的紅節點

4. 從任一結點到其所有后代葉結點的簡單路徑上,均包含相同數目的黑結點?

5. 每個葉子結點都是黑色的(此處的葉子結點指的是NIL空結點)

????????最優情況:全黑或每條路徑都是一黑一紅的滿二叉樹,高度logN

? ? ? ? 最差情況:每顆子樹左子樹全黑,右子樹一黑一紅。高度2*logN。

? ? ? ? 可以發現,最壞情況的時間復雜度和AVL樹一樣,都是O(logN),但是紅黑樹這種近似平衡的結構減少了大量旋轉,綜合性能優于AVL樹。


三、紅黑樹節點的定義

enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}
};

四、紅黑樹的插入(步驟)

1.為什么新插入的節點必須給紅色?

(1)新節點給紅色,可能出現連續紅節點

(2)如果新節點給黑色,必定會違反性質4(其每條路徑的黑色節點數量相同

2、插入紅色節點后,判定紅黑樹性質是否被破壞

因為新節點的默認顏色是紅色,所以

(1)雙親節點的顏色是黑色,沒有違反紅黑樹任何 性質,則不需要調整;

(2)雙親節點為紅色,就會出現連續的紅節點,此時需要對紅黑樹分情況來討論:見下一部分


五、插入出現連續紅節點情況分析+圖解(看uncle節點)

約定:cur為當前節點,p為父節點,g為祖父節點,u為叔叔節點

下面的分析都是以p為g的左孩子為例

5.1、uncle存在且為紅

cur插入后,p和u變黑,g變紅

(1)g沒有父親,g為根,g變黑

(2)g有父親。其為黑,結束;其為紅,后把g當成cur,繼續向上調整

5.2、uncle不存在

u不存在,則cur一定是新插入的節點

(如果cur不是新插入的節點,則cur和p一定有一個節點是黑色,否則每條路徑黑色節點不相同

下圖為解釋:

1、單旋

右單旋

2、雙旋

?左單旋 + 右單旋?

5.3、uncle存在且為黑

uncle存在且為黑,是情況一變來的,所以cur原來的節點一定是黑色的

現在其是紅色的原因是,cur的子樹在調整過程中將cur的顏色由黑變紅。

1、單旋

右單旋

2、雙旋

左單旋 + 右單旋

六、插入總結

1、紅黑樹插入的兩種步驟

1、uncle存在且為紅

2、uncle不存在 或者 uncle存在且為黑

通過分析,

uncle不存在的單旋 和 uncle存在且為黑的單旋 可以寫在一起,

uncle不存在的雙旋 和 uncle存在且為黑的雙旋 可以寫在一起,

不論uncle存在或者不存在,都不影響此步的單旋或者雙旋

當p為g的右孩子時,操作都相反。

詳細步驟見其中while (parent && parent->_col == RED)這一步。

?2、插入代碼

bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//p為g左孩子if (parent == grandfather->_left){Node* uncle = grandfather->_right;// 情況1:u存在且為紅 if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續向上處理cur = grandfather;parent = cur->_parent;}else // u不存在 或 存在且為黑{//情況2.1 , 3.1if (cur == parent->_left){//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//情況2.2 , 3.2{//     g//   p//		cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}//p為g右孩子else // parent == grandfather->_right{Node* uncle = grandfather->_left;// u存在且為紅if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續向上處理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){// g//	  p//       cRotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{// g//	  p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}

七、紅黑樹總結及代碼

紅黑樹和AVL樹都是高效的平衡二叉樹,增刪改查的時間復雜度都是O(logN),紅黑樹不追求絕對平衡,只需保證最長路徑不超過最短路徑的2倍,相對而言,降低了插入和旋轉的次數, 所以在經常進行增刪的結構中性能比AVL樹更優,而且紅黑樹實現比較簡單,所以實際運用中紅黑樹更多。

using namespace std;enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}};template<class K, class V>
struct RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//p為g左孩子if (parent == grandfather->_left){Node* uncle = grandfather->_right;// 情況1:u存在且為紅 if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續向上處理cur = grandfather;parent = cur->_parent;}else // u不存在 或 存在且為黑{//情況2.1 , 3.1if (cur == parent->_left){//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//情況2.2 , 3.2{//     g//   p//		cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}//p為g右孩子else // parent == grandfather->_right{Node* uncle = grandfather->_left;// u存在且為紅if (uncle && uncle->_col == RED){// 變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續向上處理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){// g//	  p//       cRotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{// g//	  p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateL(Node* parent){++_rotateCount;Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}void RotateR(Node* parent){++_rotateCount;Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright)curright->_parent = parent;Node* ppnode = parent->_parent;cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}bool CheckColour(Node* root, int blacknum, int benchmark){if (root == nullptr){if (blacknum != benchmark)return false;return true;}if (root->_col == BLACK){++blacknum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_kv.first << "出現連續紅色節點" << endl;return false;}return CheckColour(root->_left, blacknum, benchmark)&& CheckColour(root->_right, blacknum, benchmark);}bool IsBalance(){return IsBalance(_root);}bool IsBalance(Node* root){if (root == nullptr)return true;if (root->_col != BLACK){return false;}// 基準值int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}return CheckColour(root, 0, benchmark);}int Height(){return Height(_root);}int Height(Node* root){if (root == nullptr)return 0;int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}private:Node* _root = nullptr;public:int _rotateCount = 0;
};

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

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

相關文章

STM32 HAL庫USART串口DMA IDLE中斷編程:避坑指南

HAL_UART_Receive接收最容易丟數據了,STM32 HAL庫UART查詢方式實例 可以考慮用中斷來實現,但是HAL_UART_Receive_IT還不能直接用,容易數據丟失,實際工作中不會這樣用,STM32 HAL庫USART串口中斷編程&#xff1a;演示數據丟失, 需要在此基礎優化一下. STM32F103 HAL庫USART串口…

sql注入中information_schema被過濾的問題

目錄 一、information_schema庫的作用 二、獲得表名 2.1 sys.schema_auto_increment_columns 2.2 schema_table_statistics 三、獲得列名 join … using … order by盲注 子查詢 在進行sql注入時&#xff0c;我們經常會使用information_schema來進行爆數據庫名、表名、…

Jenkins 給任務分配 節點(Node)、設置工作空間目錄

Jenkins 給任務分配 節點(Node)、設置工作空間目錄 創建 Freestyle project 類型 任務 任務配置 Node 打開任務-> Configure-> General 勾選 Restrict where this project can be run Label Expression 填寫一個 Node 的 Label&#xff0c;輸入有效的 Label名字&#x…

Electron:使用electron-react-boilerplate創建一個react + electron的項目

使用 electron-react-boilerplate git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name cd your-project-name npm install npm start 安裝不成功 在根目錄加上 .npmrc文件 內容為 electron_…

數控機床設備分布式健康監測與智能維護系統MTAgent

數控機床設備分布式健康監測與智能維護系統MTAgent-v1.1融合了目前各種先進的信號處理以及信息分析算法以算法工具箱的方式&#xff0c;采用了一種開發的、模塊化的結構實現信號各種分析處理&#xff0c;采用Python編程語言&#xff0c;滿足不同平臺需求(包括Windows、Linux)。…

FPGA VIVADO:axi-lite 從機和主機

FPGA VIVADO:axi-lite 從機和主機 TOC在這里插入代碼片 前言 協議就不詳細講解了&#xff0c;直接看手冊即可。下面主要如何寫代碼和關鍵的時序。 此外下面的代碼可以直接用于實際工程 一、AXI-LITE 主機 數據轉axi lite接口&#xff1a; 讀/寫數據FIFO緩存 仲裁&#xff1a…

1. 對比 LVS 負載均衡群集的 NAT 模式和 DR 模式,比較其各自的優勢 。2. 基于 openEuler 構建 LVS-DR 群集。

DR 模式 * 負載各節點服務器通過本地網絡連接&#xff0c;不需要建立專用的IP隧道 原理&#xff1a;首先負載均衡器接收到客戶的請求數據包時&#xff0c;根據調度算法決定將請求發送給哪個后端的真實服務器&#xff08;RS&#xff09;。然后負載均衡器就把客戶端發送的請求數…

ollama server啟動服務后如何停止

要停止 Ollama 服務器服務&#xff0c;取決于如何啟動該服務的。以下是幾種常見的啟動方法和相應的停止服務的步驟&#xff1a; 1. 直接在命令行中啟動 如果是在命令行中直接啟動 Ollama 服務器的&#xff0c;例如使用以下命令&#xff1a; ollama serve 可以通過以下方式停…

【設計模式】03-理解常見設計模式-行為型模式(專欄完結)

前言 前面我們介紹完創建型模式和創建型模式&#xff0c;這篇介紹最后的行為型模式&#xff0c;也是【設計模式】專欄的最后一篇。 一、概述 行為型模式主要用于處理對象之間的交互和職責分配&#xff0c;以實現更靈活的行為和更好的協作。 二、常見的行為型模式 1、觀察者模…

mapbox基礎,使用geojson加載line線圖層,實現純色填充、圖片填充、虛線和漸變效果

????? 主頁: gis分享者 ????? 感謝各位大佬 點贊?? 收藏? 留言?? 加關注?! ????? 收錄于專欄:mapbox 從入門到精通 文章目錄 一、??前言1.1 ??mapboxgl.Map 地圖對象1.2 ??mapboxgl.Map style屬性1.3 ??line線圖層樣式二、??使用geojson加載…

深入淺出:CUDA是什么,如何利用它進行高效并行計算

在當今這個數據驅動的時代&#xff0c;計算能力的需求日益增加&#xff0c;特別是在深度學習、科學計算和圖像處理等領域。為了滿足這些需求&#xff0c;NVIDIA推出了CUDA&#xff08;Compute Unified Device Architecture&#xff09;&#xff0c;這是一種并行計算平臺和編程模…

LNMP+Zabbix安裝部署(Zabbix6.0 Lnmp+Zabbix Installation and Deployment)

LNMPZabbix安裝部署&#xff08;Zabbix6.0&#xff09; 簡介 LNMP&#xff08;Linux Nginx MySQL PHP&#xff09;是一種流行的Web服務器架構&#xff0c;廣泛用于搭建高性能的網站和應用程序。Zabbix 是一個開源的監控軟件&#xff0c;可以用來監控網絡、服務器和應用程序…

Docker 部署 Dify:輕松集成 Ollama 和 DeepSeek

1 Ollama的安裝及使用 1.1 什么是Ollama&#xff1f; Ollama 是一個用于本地部署和運行大型語言模型的框架。 Ollama 的作用包括&#xff1a; 本地模型運行&#xff1a;Ollama 允許在本地機器上運行大型語言模型&#xff08;如 LLaMA、DeepSeek 等&#xff09;&#xff0c;無…

C++筆記之標準庫中用于處理迭代器的`std::advance`和`std::distance`

C++筆記之標準庫中用于處理迭代器的std::advance和std::distance code review! 文章目錄 C++筆記之標準庫中用于處理迭代器的`std::advance`和`std::distance`一.`std::advance`函數原型參數說明使用場景示例代碼示例 1:移動 `std::vector` 的隨機訪問迭代器示例 2:移動 `st…

工業制造能耗管理新突破,漫途MTIC-ECM平臺助力企業綠色轉型!

在工業制造領域&#xff0c;能源消耗一直是企業運營成本的重要組成部分。隨著“雙碳”目標的推進&#xff0c;如何實現高效能耗管理&#xff0c;成為制造企業亟待解決的問題。漫途MTIC-ECM能源能耗在線監測平臺&#xff0c;結合其自研的硬件產品&#xff0c;為工業制造企業提供…

C語言——深入理解指針(2)(數組與指針)

文章目錄 數組名的理解使用指針訪問數組一維數組傳參的本質冒泡排序二級指針指針數組指針數組模擬二維數組 數組名的理解 之前我們在使用指針訪問數組內容時&#xff0c;有這樣的代碼&#xff1a; int arr[10]{1,2,3,4,5,6,7,8,9,10}; int* p&arr[0];這里我們使用&ar…

在Windows系統中安裝Open WebUI并連接Ollama

Open WebUI是一個開源的大語言模型&#xff08;LLM&#xff09;交互界面&#xff0c;支持本地部署與離線運行。通過它&#xff0c;用戶可以在類似ChatGPT的網頁界面中&#xff0c;直接操作本地運行的Ollama等大語言模型工具。 安裝前的核心要求&#xff1a; Python 3.11&#…

Day4:強化學習之Qlearning走迷宮

一、迷宮游戲 1.環境已知 迷宮環境是定義好的&#xff0c;障礙物位置和空位置是已知的&#xff1b; # 定義迷宮 grid [[0, 0, 0, 1, 0],[0, 1, 0, 1, 0],[0, 1, 0, 0, 0],[0, 0, 0, 1, 0],[0, 1, 1, 1, 0] ] 2.獎勵方式已知 如果碰到障礙物則得-1&#xff0c;如果到終點則…

家里WiFi信號穿墻后信號太差怎么處理?

一、首先在調制解調器&#xff08;俗稱&#xff1a;貓&#xff09;測試網速&#xff0c;網速達不到聯系運營商&#xff1b; 二、網線影響不大&#xff0c;5類網線跑500M完全沒問題&#xff1b; 三、可以在臥室增加輔助路由器&#xff08;例如小米AX系列&#xff09;90~200元區…

視點開場動畫實現(九)

這個相對比較簡單&#xff1a; void COSGObject::FlyTo(double lon, double lat, double hei) {theApp.bNeedModify TRUE;while(!theApp.bCanModify)Sleep(1);em->setViewpoint(osgEarth::Viewpoint("0",lon, lat, 0, 0, -45, hei), 2);theApp.bNeedModify FAL…