數據結構進階 詳談紅黑樹

目錄

1. 紅?樹的概念

紅?樹的規則

紅?樹如何確保最?路徑不超過最短路徑的2倍的?

紅?樹的效率:

2. 紅?樹的實現

紅?樹的結構

紅?樹的插?

紅?樹樹插??個值的?概過程

情況1:變?

情況2:單旋+變?

情況3:雙旋+變?

3. 紅?樹的插?代碼實現

4. 紅?樹的查找

5. 紅?樹的驗證

6. 結語


1. 紅?樹的概念

紅?樹是?棵?叉搜索樹,他的每個結點增加?個存儲位來表?結點的顏?,可以是紅?或者??。通過對任何?條從根到葉?的路徑上各個結點的顏?進?約束,紅?樹確保沒有?條路徑會?其他路徑?出2倍,因?是接近平衡的。

紅?樹的規則

1. 每個結點不是紅?就是??

2. 根結點是??的

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

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

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

紅?樹如何確保最?路徑不超過最短路徑的2倍的?

由規則4可知,從根到NULL結點的每條路徑都有相同數量的??結點,所以極端場景下,最短路徑就就是全是??結點的路徑,假設最短路徑?度為bh(black height)。

由規則2和規則3可知,任意?條路徑不會有連續的紅?結點,所以極端場景下,最?的路徑就是???紅間隔組成,那么最?路徑的?度為2*bh。

綜合紅?樹的4點規則??,理論上的全?最短路徑和???紅的最?路徑并不是在每棵紅?樹都存在的。假設任意?條從根到NULL結點路徑的?度為x,那么bh <= h <= 2*bh。

紅?樹的效率:

假設N是紅?樹樹中結點數量,h最短路徑的?度,那么 h ? 1 <= N < 2 ^2?h ? 1, 由此推出h logN ,也就是意味著紅?樹增刪查改最壞也就是?最?路徑2 ? logN,那么時間復雜度還O(logN)

紅?樹的表達相對AVL樹要抽象?些,AVL樹通過?度差直觀的控制了平衡。紅?樹通過4條規則的顏?約束,間接的實現了近似平衡,他們效率都是同?檔次,但是相對??,插?相同數量的結點,紅?樹的旋轉次數是更少的,因為他對平衡的控制沒那么嚴格。

2. 紅?樹的實現

紅?樹的結構

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

紅?樹的插?

紅?樹樹插??個值的?概過程

1. 插??個值按?叉搜索樹規則進?插?,插?后我們只需要觀察是否符合紅?樹的4條規則。

2. 如果是空樹插?,新增結點是??結點。如果是?空樹插?,新增結點必須紅?結點,因為?空樹插?,新增??結點就破壞了規則4,規則4是很難維護的。

3. ?空樹插?后,新增結點必須紅?結點,如果?親結點是??的,則沒有違反任何規則,插?結束

4. ?空樹插?后,新增結點必須紅?結點,如果?親結點是紅?的,則違反規則3。進?步分析,c是紅?,p為紅,g必為?,這三個顏?都固定了,關鍵的變化看u的情況,需要根據u分為以下?種情況分別處理。

說明:下圖中假設我們把新增結點標識為c (cur),c的?親標識為p(parent),p的?親標識為g(grandfather),p的兄弟標識為u(uncle)

情況1:變?

c為紅,p為紅,g為?,u存在且為紅,則將p和u變?,g變紅。在把g當做新的c,繼續往上更新。

分析:因為p和u都是紅?,g是??,把p和u變?,左邊?樹路徑各增加?個??結點,g再變紅,相當于保持g所在?樹的??結點的數量不變,同時解決了c和p連續紅?結點的問題,需要繼續往上更新是因為,g是紅?,如果g的?親還是紅?,那么就還需要繼續處理;如果g的?親是??,則處理結束了;如果g就是整棵樹的根,再把g變回??。

情況1只變?,不旋轉。所以?論c是p的左還是右,p是g的左還是右,都是上?的變?處理?式。

跟AVL樹類似,圖0我們展?了?種具體情況,但是實際中需要這樣處理的有很多種情況。

圖1將以上類似的處理進?了抽象表達,d/e/f代表每條路徑擁有hb個??結點的?樹,a/b代表每 條路徑擁有hb-1個??結點的根為紅的?樹,hb>=0。

圖2/圖3/圖4,分別展?了hb == 0/hb == 1/hb == 2的具體情況組合分析,當hb等于2時,這?組合 情況上百億種,這些樣例是幫助我們理解,不論情況多少種,多么復雜,處理?式?樣的,變?再 繼續往上處理即可,所以我們只需要看抽象圖即可

情況2:單旋+變?

c為紅,p為紅,g為?,u不存在或者u存在且為?,u不存在,則c?定是新增結點,u存在且為?,則c?定不是新增,c之前是??的,是在c的?樹中插?,符合情況1,變?將c從??變成紅?,更新上來的。

分析:p必須變?,才能解決,連續紅?結點的問題,u不存在或者是??的,這?單純的變??法解決問題,需要旋轉+變?。

? ? ? ? ? g

? ? p? ? ? ? u

c

如果p是g的左,c是p的左,那么以g為旋轉點進?右單旋,再把p變?,g變紅即可。p變成課這顆樹新的根,這樣?樹??結點的數量不變,沒有連續的紅?結點了,且不需要往上更新,因為p的?親是??還是紅?或者空都不違反規則。

? ? ? ? g

? ? u? ? ? p

? ? ? ? ? ? ? ?c

如果p是g的右,c是p的右,那么以g為旋轉點進?左單旋,再把p變?,g變紅即可。p變成課這顆樹新的根,這樣?樹??結點的數量不變,沒有連續的紅?結點了,且不需要往上更新,因為p的?親是??還是紅?或者空都不違反規則。

情況3:雙旋+變?

c為紅,p為紅,g為?,u不存在或者u存在且為?,u不存在,則c?定是新增結點,u存在且為?,則c?定不是新增,c之前是??的,是在c的?樹中插?,符合情況1,變?將c從??變成紅?,更新上來的。

分析:p必須變?,才能解決,連續紅?結點的問題,u不存在或者是??的,這?單純的變??法解決問題,需要旋轉+變?。

? ? ? ? ? g

? ? p? ? ? ? ?u

? ? ? c

如果p是g的左,c是p的右,那么先以p為旋轉點進?左單旋,再以g為旋轉點進?右單旋,再把c變 ?,g變紅即可。c變成課這顆樹新的根,這樣?樹??結點的數量不變,沒有連續的紅?結點了,且不需要往上更新,因為c的?親是??還是紅?或者空都不違反規則。

? ? ? ? g

? ? u? ? ? ?p

? ? ? ?c

如果p是g的右,c是p的左,那么先以p為旋轉點進?右單旋,再以g為旋轉點進?左單旋,再把c變 ?,g變紅即可。c變成課這顆樹新的根,這樣?樹??結點的數量不變,沒有連續的紅?結點了,且不需要往上更新,因為c的?親是??還是紅?或者空都不違反規則。

3. 紅?樹的插?代碼實現

// 旋轉代碼的實現跟AVL樹是?樣的,只是不需要更新平衡因?
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;// g// p uif (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// u存在且為紅 -》變?再繼續往上處理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{// u存在且為?或不存在 -》旋轉+變?if (cur == parent->_left){// g// p u//c//單旋RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// p u// c//雙旋RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{// g// u pNode* uncle = grandfather->_left;// 叔叔存在且為紅,-》變?即可if (uncle && uncle->_col == RED){parent->_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;}else{// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}

4. 紅?樹的查找

按?叉搜索樹邏輯實現即可,搜索效率為 O(logN)

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;
}

5. 紅?樹的驗證

這?獲取最?路徑和最短路徑,檢查最?路徑不超過最短路徑的2倍是不可?的,因為就算滿?這個條件,紅?樹也可能顏?不滿?規則,當前暫時沒出問題,后續繼續插?還是會出問題的。所以我們還是去檢查4點規則,滿?這4點規則,?定能保證最?路徑不超過最短路徑的2倍。

1. 規則1枚舉顏?類型,天然實現保證了顏?不是??就是紅?。

2. 規則2直接檢查根即可

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

4. 規則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);
}

6. 結語

這里分享一個b站Up主關于紅黑樹的視頻資料~

紅黑樹 - 定義, 插入, 構建_嗶哩嗶哩_bilibilihttps://www.bilibili.com/video/BV1Xm421x7Lg/?spm_id_from=333.1387.search.video_card.click

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

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

相關文章

【MySQL】MySQL去重查詢詳解

前言 在日常的數據庫操作中&#xff0c;數據去重是一個非常常見的需求。無論是查詢結果去重、數據清洗&#xff0c;還是統計分析&#xff0c;我們都需要掌握MySQL中的各種去重技術。本文將詳細介紹MySQL中常用的去重關鍵字和操作方法&#xff0c;結合實際業務場景&#xff0c;幫…

Pinterest視覺營銷自動化:亞矩陣云手機實例與多分辨率適配技術

Pinterest月活突破4.5億的視覺經濟時代&#xff0c;多分辨率適配與跨設備一致性成為品牌觸達用戶的核心挑戰。傳統營銷因素材模糊、設備參數固化&#xff08;如固定分辨率1080P&#xff09;、行為機械化&#xff08;如定時批量上傳&#xff09;&#xff0c;導致點擊率低于行業均…

01數據結構-圖的鄰接矩陣和遍歷

01數據結構-圖的鄰接矩陣和遍歷1.圖的遍歷1.1深度優先遍歷1.2廣度優先搜索2.鄰接矩陣的代碼實現1.圖的遍歷 1.1深度優先遍歷 深度優先搜索的過程類似于樹的先序遍歷&#xff0c;首先從例子中體會深度優先搜索&#xff0c;例如下圖1是個無向圖&#xff0c;采用深度優先算法遍歷…

OpenAI發布的GPT-5 更新了哪些內容,它的核心能力有哪些?AI編碼能力這么強,前端程序員何去何從?

目錄**1. GPT-5的核心能力與技術突破****1.1 智能水平的質變****1.2 代碼生成與優化****1.3 上下文處理與長文本能力****1.4 安全與可靠性改進****2. GPT-5的應用場景與案例****2.1 醫療領域****2.2 教育與學習****2.3 企業級應用****2.4 軟件開發****3. 技術細節與創新****3.1…

【無標題】AI 賦能日常效率:實用案例與操作心得分享

大語言模型&#xff08;LLM&#xff09;早已不再是實驗室里的專屬品&#xff0c;而是逐漸滲透到我們工作與生活的方方面面。從繁瑣的文檔處理到復雜的信息篩選&#xff0c;從學習輔助到日常規劃&#xff0c;AI 正以 "微生產力" 的形式重塑我們的效率邊界。本文將分享…

Java-線程線程的創建方式

一.進程和線程進程&#xff1a;進程是資源分配的基本單位&#xff0c;每個進程都有自己獨立的內存空間&#xff0c;可以看作是一個正在運行的程序實例線程&#xff1a;線程是CPU調度的基本單位&#xff0c;屬于進程&#xff0c;一個進程可以包含多個線程。線程共享進程的內存空…

Electron 中 license-keys 的完整集成方案

secure-electron-license-keys 是一個專門為 Electron 應用設計的 npm 包&#xff0c;用于實現離線許可證密鑰的創建、驗證和管理&#xff0c;幫助開發者保護應用程序&#xff0c;確保只有擁有合法許可證的用戶才能使用。以下是關于它的詳細介紹&#xff1a; 在 Electron 應用中…

AI推理的“靈魂五問”:直面2025算力鴻溝與中國的破局之路

摘要&#xff1a;2025年&#xff0c;AI產業的重心已從訓練全面轉向推理&#xff0c;但一場嚴峻的“體驗”危機正悄然上演。中美AI推理性能的巨大鴻溝&#xff0c;正讓國內廠商面臨用戶流失的切膚之痛。本文以問答形式&#xff0c;直面當前中國AI產業在推理“最后一公里”上最尖…

2025 TexLive+VScode排版IEEE TGRS論文

2025 TexLiveVScode排版IEEE TGRS論文 本文主要內容&#xff1a; 軟件安裝 latex 排版 TRGS 論文期間遇到的問題 清晰圖片導出 Latex公式、圖、表、算法、參考文獻的使用和引用 1. 前言 首先使用Overleaf網頁版排版&#xff0c;但是后期排版圖片太大&#xff0c;大小有限制&…

Redis數據組織方式

前言 Redis之所以高效&#xff0c;源自其優秀的架構設計。作為KV鍵值對存儲數據庫&#xff0c;數據的存儲放在了內存中&#xff0c;KV鍵值對的組織方式更是其高效的原因之一。本文介紹其數據組織方式。 一、總體架構 在使用Redis時&#xff0c;服務端接收多個客戶端的命令進行…

java組件安全vulhub靶場

>1--XStream1.打開靶場cd vulhub-master/xstream/CVE-2021-29505 docker up -d2.下載反序列化工具https://github.com/frohoff/ysoserial可以使用clone命令進行下載&#xff0c;也可以直接下載jar文件3.使用以下命令來開啟腳本&#xff0c;將是反彈shell的語句進行base64編碼…

UCMT部分復現

復現結果&#xff1a;88.03272&#xff0c;誤差在接受范圍內 補充信息 作者未解決后續報錯問題&#xff0c;不建議復現

IntelliJ IDEA 新手全方位使用指南

摘要本文面向剛接觸軟件開發、使用 IntelliJ IDEA 的新手&#xff0c;詳細介紹了 IDEA 的背景、版本區別、核心功能、運行原理、界面操作、項目管理、運行配置、以及 Git 版本控制基礎。文章突出實用操作和理解流程&#xff0c;幫助新手快速熟悉IDEA環境&#xff0c;順利完成項…

Python如何將圖片轉換為PDF格式

引言 在日常工作和學習中&#xff0c;我們經常需要將多張圖片合并成一個PDF文件&#xff0c;以便于分享或打印。Python提供了多種庫來實現這一需求&#xff0c;本文將詳細介紹三種常用的方法&#xff1a;img2pdf庫、Pillow庫和PyMuPDF庫&#xff0c;并附上完整的代碼示例。 方法…

Python如何合并兩個Excel文件

引言 在日常數據處理中&#xff0c;合并Excel文件是常見需求。Python提供了多種庫&#xff08;如pandas、openpyxl&#xff09;來實現這一操作。本文將詳細介紹兩種主流方法&#xff0c;并附上完整代碼示例&#xff0c;幫助您高效完成Excel合并任務。 方法一&#xff1a;使用pa…

【SQL進階】用EXPLAIN看透SQL執行計劃:從“盲寫“到“精準優化“

用EXPLAIN洞察SQL執行計劃&#xff1a;從"盲目編寫"到"精準優化" 很多開發者在編寫SQL時僅憑直覺&#xff0c;直到查詢超時才發現問題。MySQL內置的EXPLAIN工具能提前揭示查詢執行邏輯&#xff0c;幫助預防性能隱患。本文將帶你掌握EXPLAIN的核心用法&…

電影藝術好,電影知識得學

關于電影應該談什么導演風格、演員技術、劇本結構、票房、政治因素等。一、紙上談電影電影制作期&#xff1a;研發、前制、拍攝、后制、發行。一般成員只在某個時期出現。制片和導演會從頭監督到尾。研發期&#xff1a; 劇本概念發想與成形的時期。創作自由度比較大&#xff0c…

FPGA學習筆記——簡易的DDS信號發生器

目錄 一、任務 二、分析 三、ROM IP核配置 四、Visio圖 五、代碼 &#xff08;1&#xff09;.v代碼 &#xff08;2&#xff09;仿真代碼 六、仿真 七、實驗現象 一、任務 用串口模塊&#xff0c;用上位機發送指令&#xff0c;FPGA接收&#xff0c;然后輸出對應的波形&…

在NVIDIA Orin上用TensorRT對YOLO12進行多路加速并行推理時內存泄漏 (中)

接上篇 在NVIDIA Orin上用TensorRT對YOLO12進行多路加速并行推理時內存泄漏&#xff08;上&#xff09; 通過上篇的分析&#xff0c;發現問題在采集數據到傳入GPU之前的階段。但隨著新一輪長時間測試發現&#xff0c;問題依然存在。 如上圖&#xff0c;在運行20多分鐘內存開始…

計數組合學7.17(Murnaghan–Nakayama 規則 )

7.17 Murnaghan–Nakayama 規則 我們已經成功地用基 mλm_\lambdamλ?、hλh_\lambdahλ? 和 eλe_\lambdaeλ? 表示了 Schur 函數 sλs_\lambdasλ?。本節我們將考慮冪和對稱函數 pλp_\lambdapλ?。一個斜分劃 λ/μ\lambda / \muλ/μ 是連通的&#xff0c;如果其分拆圖…