C++(初階)(十九)——紅黑樹

紅黑樹

  • 紅黑樹
    • 概念
    • 規則
    • 實現
      • 結點
      • 插入
      • 變色
        • 變色參考代碼:
      • 查找
        • 查找參考代碼
      • 遍歷
    • 紅黑樹檢查
    • 完整代碼

概念

紅?樹是?棵?叉搜索樹。它的每個結點增加?個存儲位來表示結點的顏?,可以是紅色或者黑色(并不會出現第三種顏色)。

通過對結點顏色特別的規則進行約束,紅黑樹確保沒有任何?條路徑會比其他路徑長出2倍,即保證最長路徑(一黑一紅)<= 最短路徑*2(全黑),因此紅黑樹是接近平衡的。

規則

1,每個結點不是紅色就是黑色。

2,根結點是黑色的。

3,如果?個結點是紅?的,則它的兩個孩?結點必須是黑色的,也就是說任意?條路徑不會有連續的紅色結點。

4,對于任意?個結點,從該結點到其所有空結點(NULL)的簡單路徑上,均包含相同數量的黑色結點。

5,最長路徑(一黑一紅)<= 最短路徑*2(全黑)。

說明:《算法導論》等書籍上補充了?條每個葉?結點(NIL)都是黑色的規則。他這?所指的葉?結點 不是傳統的意義上的葉?結點,?是我們說的空結點,有些書籍上也把NIL叫做外部結點。NIL是為了 ?便準確的標識出所有路徑,《算法導論》在后續講解實現的細節中也忽略了NIL結點,所以我們知道 ?下這個概念即可

實現

結點

// 枚舉值表?顏?
enum Colour
{RED,BLACK
};// 這?我們默認按key_value結構實現
template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;//這?更新控制平衡也要加?parent指針RBTreeNode<K, V>* _parent;Colour _col;//構造RBTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(BLACK){}
};

插入

首先實現最簡單的插入操作。

1,首先判斷是否是空樹,因為空樹也是紅黑樹。如果是空樹,創建新節點,顏色是黑色(根節點必須是黑色),返回即可。

2,查找我們數據應該插入的位置,查找規則和二叉搜索樹一樣。比當前結點大,再去和右孩子的結點比較;比當前結點小,再去和左孩子的結點比較;即大就向右走,小就向左走,直到找到空,執行插入操作。

3,不是空樹,創建新增結點,默認顏色是紅色。為什么不給黑色,仔細分析紅黑樹的規則發現:如果新增結點是黑色的話,那么就可能會破壞其他簡單路徑上黑色結點數量,造成簡單路徑黑色結點的數量不相等。所以默認是紅色結點,這樣更方便。

那是否可以將全部結點都設置為黑色呢?按照紅黑樹的規則來看,邏輯上沒有問題,但是這樣的紅黑樹就失去了原本意義,全部都是黑色結點和二叉搜索樹沒有區別,而且還浪費了存儲顏色的額外空間。

4,最簡單的情況是,新節點的父親結點是黑色,則插入成功后就結束。

但是如果父親結點是紅色,那么插入新節點后就需要進行變色處理,原因是紅黑樹不會存在連續的紅色結點,變色處理稍微麻煩,放到后邊位置詳細說明。

參考代碼:

template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public://插入bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}//記錄cur的父結點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;return true;}private:Node* _root = nullptr;
};

變色

前面分析過,如果新節點的父親結點存在且為紅色,那么需要對祖先結點進行變色。

但是變色依舊有多種情況,需要分別討論。

為了方便使用縮寫展示:g代表grandfather,p代表parent,u代表uncle,c代表cur即新結點。

1,

  • 父親結點存在且為紅色
  • 此時的父親結點是爺爺結點的左孩子
  • 叔叔結點存在并且為紅色

在這里插入圖片描述

此時經過分析得到,我們需要對父親結點進行變色處理,以下是參考過程:

  • 將父親和叔叔結點顏色變黑,爺爺結點顏色變紅

  • 注意:為了防止此處將根結點也變為紅色,所以在最后一處_root->_col = RED

  • 如果此時符合紅黑樹的規則,那么即可結束,反之向上更新結點,直到結束。

//更新結點位置,向上更新
cur = grandfather;
parent = cur->_parent;

2,

  • 父親結點存在且為紅色
  • 此時的父親結點是爺爺結點的左孩子
  • 叔叔結點不存在或者存在且為黑色

此時需要進行變色+旋轉處理。

但是旋轉時又有所差異:如果cur是parent的左孩子(如下圖),需要以grandfather為旋轉點進行右單旋;

如果cur是parent的右孩子(如下圖),需要先以parent為旋轉點左單旋,再以grandfather為旋轉點進行右單旋的左右雙旋,最后要記得將旋轉后的cur改為黑色,grandfather改為紅色。

3,

  • 父親結點存在且為紅色

  • 此時的父親結點是爺爺結點的右孩子

  • 叔叔結點存在且為紅色

與情況1類似不再贅述。

4,

  • 父親結點存在且為紅色

  • 此時的父親結點是爺爺結點的右孩子

  • 叔叔結點不存在或者存在且為黑色

與情況2類似不再贅述。

變色參考代碼:
		//變色處理while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//  g//p   uif (parent == grandfather->_left){Node* uncle = grandfather->_right;//叔叔結點存在,并且為紅色if (uncle && uncle->_col == RED){//將父親和叔叔結點顏色變黑,爺爺結點顏色變紅//為了防止此處將根結點也變為紅色,所以在最后一處_root->_col = REDparent->_col = uncle->_col = BLACK;grandfather->_col = RED;//更新結點位置,向上更新cur = grandfather;parent = cur->_parent;}//叔叔結點不存在,或者存在并且為黑色//變色+旋轉else{//    g//  p   u//cif (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//cur == parent->_rightelse{//    g//  p   u//    c//雙旋RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}//    g//  u   p//parent == grandfather->_rightelse{Node* uncle = grandfather->_left;//叔叔結點存在,并且為紅色if (uncle && uncle->_col == RED){//將父親和叔叔結點顏色變黑,爺爺結點顏色變紅//為了防止此處將根結點也變為紅色,所以在最后一處_root->_col = REDparent->_col = uncle->_col = BLACK;grandfather->_col = RED;//更新結點位置,向上更新cur = grandfather;parent = cur->_parent;}//叔叔結點不存在,或者存在并且為黑色//變色+旋轉else{//    g//  u   p//		   cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//cur == parent->_leftelse{//    g//  p   u//    c//雙旋RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;

查找

查找很簡單,與二叉搜索樹的查找規則一樣。

即要查找的cur結點的值大于根結點就向右走,比當前結點小就向左走,直到查找到或者走到空為止。

查找參考代碼
//查找
Node* Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;
}

遍歷

因為紅黑樹是二叉搜索樹,所以使用中序遍歷即可。

//中序遍歷
void _InOrder(Node* root)
{if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);
}

紅黑樹檢查

對紅黑樹檢查時,可以從紅黑樹的規則入手。

1,每個結點不是紅色就是黑色。

2,根結點是黑色的。

3,如果?個結點是紅?的,則它的兩個孩?結點必須是??的,也就是說任意?條路徑不會有連續的紅色結點。

4,對于任意?個結點,從該結點到其所有NULL結點的簡單路徑上,均包含相同數量的??結點。

對于規則1,我們在實現時,使用的就是紅色和黑色的枚舉,不可能出現其他顏色。

對于規則2,也很好判斷,加一句判斷語句即可。

對于規則3,前序遍歷檢查,遇到紅?結點查孩?不太?便,因為孩?有兩個,且不?定存在,反過來檢查?親的顏?就?便多了。

對于規則4,前序遍歷,遍歷過程中?形參記錄跟到當前結點的blackNum(??結點數量),前序遍歷遇到??結點就++blackNum,?到空就計算出了?條路徑的??結點數量。再任意?條路徑??結點 數量作為參考值,依次?較即可。

bool Check(Node* root, int blackNum, const int refNum)
{if (root == nullptr){//前序遍歷?到空時,意味著?條路徑?完了//cout << blackNum << endl;if (refNum != blackNum){cout << "存在??結點的數量不相等的路徑" << endl;return false;}return true;}//因為孩子結點可能有兩個,也可能不存在,所以檢查父親結點更方便if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "存在連續的紅色結點" << endl;return false;}if (root->_col == BLACK){++blackNum;}return Check(root->_left, blackNum, refNum)&& Check(root->_right, blackNum, refNum);
}bool IsBalance()
{//首先,如果是空樹也是紅黑樹if (_root == nullptr){return true;}//再者,如果根結點是紅色,不是紅黑樹if (_root->_col == RED){return false;}//剩余情況,需要依照參考值判斷//參考值int refNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refNum;}cur = cur->_left;}return Check(_root, 0, refNum);
}

完整代碼

https://gitee.com/any10/c_plus_plus/blob/master/2025c%2B%2B/RBTree_5_5/RBTree.h

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

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

相關文章

Mistral AI 開源最新 Small 模型——Devstral-Small-2505

Devstral 是一款專為軟件工程任務設計的代理型大語言模型&#xff08;LLM&#xff09;&#xff0c;由 Mistral AI 和 All Hands AI 合作開發 &#x1f64c;。Devstral 擅長使用工具探索代碼庫、編輯多個文件以及驅動軟件工程代理。該模型在 SWE-bench 上表現出色&#xff0c;使…

CDGA|一線二線企業數據治理項目目前發展狀況

一線城市與二線城市企業在數據治理項目的發展狀況上存在一定差異&#xff0c;主要體現在目標、資源投入、策略實施以及文化培育等方面。 一線城市企業數據治理項目發展狀況 ?數據治理目標全面系統?&#xff1a; ?數據質量與安全?&#xff1a;一線城市的大型企業通常擁有海量…

Lyra學習筆記1地圖角色加載流程

目錄 1 地圖加載流程1.1 默認Experience的加載1.2 加載角色1.3 加載場景中的幾個傳送點 2 幾個內建類的筆記2.1 UDataAsset2.2 UAssetManager 純個人筆記&#xff0c;有錯誤歡迎指正&#xff0c;學習階段基本看到不會的就寫一寫&#xff0c;最后有時間會梳理整體結構 先看完了官…

SurfaceFlinger及Android應用RenderThread角度觀察Jank丟幀卡頓

SurfaceFlinger及Android應用RenderThread角度觀察Jank丟幀卡頓 CPU、GPU、Display 三個部分&#xff1a;CPU 負責計算幀數據&#xff0c;把計算好的數據交給 GPU&#xff0c;GPU 會對圖形數據進行渲染&#xff0c;渲染好后放到 buffer &#xff08;圖像緩沖區&#xff09;存起…

《牛客》數組中出現次數超過一半的數字

牛客的刷題之路不停歇 ??? 不積跬步無以至千里&#xff0c;不積小流無以成江海 The harder you work,the luckier you will be 題目及示例 題目鏈接 描述 給一個長度為 n 的數組&#xff0c;數組中有一個數字出現的次數超過數組長度的一半&#xff0c;請找出這個數字。 例…

七彩喜康養護理——科技賦能下的全周期健康守護

在當今社會&#xff0c;隨著人們健康意識的不斷提高&#xff0c;護理行業逐漸走向專業化、精細化&#xff0c;而七彩喜智養護理作為一種新興的護理方式&#xff0c;逐漸受到了廣泛的關注和應用。 它不僅僅是針對單一病癥的治療護理&#xff0c;而是一種全面的、全方位的健康管…

【爬蟲】12306自動化購票

上文&#xff1a; 【爬蟲】12306查票-CSDN博客 下面是簡單的自動化進行搶票&#xff0c;只寫到預定票&#xff0c;沒有寫完登陸&#xff0c; 跳出登陸后與上述代碼同理修改即可。 感覺xpath最簡單&#xff0c;復制粘貼&#xff1a; 還有很多寫法&#xff1a; 官網地址&#…

Java設計模式之組合模式:從入門到精通(保姆級教程)

文章目錄 1. 組合模式概述1.1 專業定義1.2 通俗解釋1.3 模式結構2. 組合模式詳細解析2.1 模式優缺點2.2 適用場景3. 組合模式實現詳解3.1 基礎實現3.2 代碼解析4. 組合模式進階應用4.1 透明式 vs 安全式組合模式4.2 組合模式與遞歸4.3 組合模式與迭代器5. 組合模式在實際開發中…

游戲如何應對反編譯工具dnspy

Unity Mono 是 Unity 引擎默認的腳本運行時環境&#xff0c;由跨平臺的開源 .NET 框架實現&#xff0c;它允許開發者使用 C# 等編程語言編寫游戲邏輯&#xff0c;憑借簡單易用的開發環境和高效的腳本編譯速度&#xff0c;得到了眾多游戲的青睞。 在 Mono 模式下&#xff0c;游…

騰訊云證書過期提醒的應對措施,Caddy 自動管理的 Let‘s Encrypt 證書.

用騰訊的免費證書&#xff0c;90天需要換一次。 Caddy 自動管理的 Lets Encrypt 證書. 在網站上按F12然后找到security選項&#xff0c;然后選擇View certifcate 就可以看到證書的有效期。 完全無需操作 你的網站實際使用的是 Caddy 自動管理的 Lets Encrypt 證書&#xff0c;…

[Java實戰]Spring Boot整合Elasticsearch(二十六)

[Java實戰]Spring Boot整合Elasticsearch&#xff08;二十六&#xff09; 摘要&#xff1a;本文通過完整的實戰演示&#xff0c;詳細講解如何在Spring Boot項目中整合Elasticsearch&#xff0c;實現數據的存儲、檢索和復雜查詢功能。包含版本適配方案、Spring Data Elasticsea…

【關聯git本地倉庫,上傳項目到github】

目錄 1.下載git2.綁定用戶3.git本地與遠程倉庫交互4.github項目創建5.上傳本地項目到github6.完結撒花???&#xff01;&#xff01;&#xff01; 1.下載git git下載地址&#xff1a;https://git-scm.com/downloads 下載安裝后創建快捷地址&#xff1a;&#xff08;此處比較…

[Vue]路由基礎使用和路徑傳參

實際項目中不可能就一個頁面&#xff0c;會有很多個頁面。在Vue里面&#xff0c;頁面與頁面之間的跳轉和傳參會使用我們的路由: vue-router 基礎使用 要使用我們需要先給我們的項目添加依賴:vue-router。使用命令下載: npm install vue-router 使用路由會涉及到下面幾個對象:…

軟考-軟件工程開發模型

軟考-軟件工程開發模型 參考視頻&#xff1a; 軟件工程概述&開發模型 &#xff0c;配合視頻理解更清晰&#xff5e; 軟件的生命周期為&#xff1a;需求分析、軟件設計、軟件開發、運行維護直至被淘汰 幾個階段。 軟件工程支持 4 個活動&#xff0c;簡稱 PDCA&#xff0c…

【寫在創作紀念日】基于SpringBoot和PostGIS的各省東西南北四至極點區縣可視化

目錄 前言 一、空間檢索簡介 1、空間表結構 2、四至空間檢索 二、前后端實現 1、后端實現 2、前端集成 三、成果展示 1、東部省份 2、西部省份 3、南部省份 4、北部省份 5、中部省份 四、總結 前言 在當今數字化時代&#xff0c;地理信息數據的分析與可視化對于眾…

智能守護校園“舌尖安全“:AI視頻分析賦能名廚亮灶新時代

引言&#xff1a; 在校園食品安全備受關注的今天&#xff0c;一套融合視頻監控管理平臺與AI視頻分析盒子的智能解決方案正在全國多地學校食堂悄然落地&#xff0c;為傳統的"名廚亮灶"工程注入科技新動能。這套系統不僅實現了后廚操作的"透明化"&#xff0…

【軟件設計師】計算機網絡考點整理

以下是軟件設計師考試中 ??計算機網絡?? 的核心考點總結&#xff0c;幫助您高效備考&#xff1a; ??一、網絡體系結構與協議?? ??OSI七層模型 & TCP/IP四層模型?? 各層功能&#xff08;物理層-數據鏈路層-網絡層-傳輸層-會話層-表示層-應用層&#xff09;對應協…

Starrocks的CBO基石--統計信息的來源 StatisticAutoCollector

背景 本文來從底層代碼的實現來分析一下Starrocks怎么獲取統計信息&#xff0c;這些統計信息在后續基于CBO的代價計算的時候有著重要的作用 本文基于Starrrocks 3.3.5 結論 Starrocks的統計信息的收集是通過周期性的運行一系列的SQL&#xff08;以分區為維度&#xff0c;如果…

深度學習模型部署(四)——RKNN

一、RKNN部署及工具包安裝 參考1&#xff1a;https://blog.csdn.net/qq_40280673/article/details/136211086#/ 參考2&#xff1a;瑞芯微官方教程 RKNN部署針對瑞芯微芯片優化&#xff0c;支持NPU硬件加速&#xff0c;需要安裝rknn-toolkit&#xff0c;用于將pytorch模型轉換為…

重構研發效能:項目管理引領軟件工廠邁向智能化

1.項目管理智能化&#xff0c;激活軟件工廠新引擎 在高速發展的軟件開發時代&#xff0c;企業如何高效管理多個項目、協調團隊合作、優化資源配置&#xff0c;已成為推動技術進步的關鍵。尤其是在多任務、多項目并行的復雜環境下&#xff0c;智能項目組合管理工具正成為軟件工…