C++_紅黑樹

? ? ? ?

目錄

1、紅黑樹的規則

2、紅黑樹節點的定義?

3、紅黑樹插入節點的調整操作

3.1 情況一

3.2 情況二

3.3 情況三?

4、紅黑樹的實現

結語


前言:

????????在C++中,紅黑樹是二叉搜索樹的另一種優化版本,他與AVL樹的區別在于保持樹的平衡方式不同,AVL樹保持平衡的方式是在節點中多存儲一個成員來記錄平衡因子,紅黑樹保持平衡的方式也是增加了一個成員,但是該成員的作用是記錄節點的兩種狀態(顏色):紅色--黑色。當然只記錄顏色并不能保持平衡,紅黑樹還規定最長路徑的節點個數不會超過最短路徑的節點個數的兩倍,因此紅黑樹不會因為插入有序數據而演變成“單支樹”。

1、紅黑樹的規則

? ? ? ? 紅黑樹有如下規則:

? ? ? ? 1、顧名思義,紅黑樹的節點只能有黑色和紅色兩種狀態。

? ? ? ? 2、根結點默認為黑色。

? ? ? ? 3、紅色節點的兩個孩子只能是黑色節點。

? ? ? ? 4、插入的節點默認為紅色節點。

? ? ? ? 5、每條路徑的黑色節點都相同。

? ? ? ? 紅黑樹正確示意圖:

? ? ? ? ?紅黑樹錯誤示意圖:

2、紅黑樹節點的定義?

? ? ? ? 通過上述對紅黑樹的簡述,可以給出紅黑樹的節點代碼:

#define _CRT_SECURE_NO_WARNINGS 1enum 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;//若是AVL樹這里記載的是平衡因子,紅黑樹是顏色RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED)//默認插入的節點是紅色{}
};

? ? ? ? 可以發現,紅黑樹的節點代碼幾乎和AVL樹一模一樣,只是控制平衡的條件有區別,僅此而已。?

3、紅黑樹插入節點的調整操作

? ? ? ? 紅黑樹的插入函數可以分兩個步驟:

? ? ? ? 1、找到合適的位置插入,即二叉搜索樹插入的邏輯(小于根節點的放在左邊,大于根節點的放在右邊)。

? ? ? ? 2、因為插入的節點默認為紅色,則插入節點后,查看當前樹是否破壞了紅黑樹的規則,即觀察其節點的父母節點是否為紅色,如果是則需要進行調整操作(規則3)。

? ? ? ? 在分析之前,先確定好節點之間的關系名稱(cur表示新插入的節點,parant表示父母節點,uncle表示叔叔節點,gparent表示祖父節點):

3.1 情況一

????????當叔叔節點存在且為紅,父母節點為紅,祖父節點為黑:

? ? ? ? 最后可以發現,經過一系列的調整后符號紅黑樹的規則。

3.2 情況二

? ? ? ? 情況二又分兩種情況:1、叔叔節點為黑色。2、不存在叔叔節點。

? ? ? ? 1、其他條件和情況一相同,但是叔叔節點是黑色的:

? ? ? ? 從上圖可以發現,情況二多了旋轉的步驟,并且在旋轉之后將parent變黑,gparent變紅,最終結果滿足紅黑樹的規則。

? ? ? ? 2、若不存在叔叔節點:

? ? ? ? 綜上所述,情況二可以總結為:旋轉+變色(parent變黑,gparent變紅)。?

3.3 情況三?

? ? ? ? 情況三即以上情況的插入點不一樣,以上情況的插入節點都是插入在“邊緣處”,通俗來講就是左子樹插入的節點都是作為左孩子插入的,而右子樹插入的節點都是作為右孩子插入的,但是實際中總會出現在右子樹中插入的節點是以左孩子的形式插入的(如下圖),拿上述情況二的第二種情況舉例,若插入的cur在parent的左邊,那么以上的處理方法顯然不能解決問題,具體操作圖如下:

4、紅黑樹的實現

? ? ? ? 紅黑樹的實現代碼如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
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;//若是AVL樹這里記載的是平衡因子,紅黑樹是顏色RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED)//默認插入的節點是紅色{}
};template<class K, class V>
class RBTree//紅黑樹類
{typedef RBTreeNode<K, V> Node;
public:~RBTree(){_Destroy(_root);_root = nullptr;}
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);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//當parent為紅色則違法規則,需要調整while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent)//父母在祖父的左邊{Node* uncle = grandfather->_right;// 情況1:叔叔節點存在且為紅,叔叔和父母進行變色處理,并繼續往上處理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上調整cur = grandfather;parent = cur->_parent;}else // 情況2:叔叔不存在/叔叔存在且為黑,旋轉+變色{//父母在祖父的左邊,而cur在父母的左邊,說明是“邊緣”情況if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//對應所述的情況三,需要旋轉兩次,并且cur變成了根結點{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else // 父母在祖父的右邊{Node* uncle = grandfather->_left;// 情況1:u存在且為紅,變色處理,并繼續往上處理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上調整cur = grandfather;parent = cur->_parent;}else // 情況2:叔叔不存在/叔叔存在且為黑,旋轉+變色{//以下邏輯同上思路,只不過邏輯相反if (cur == parent->_right){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;//根結點始終為黑return true;}void InOrder(){_InOrder(_root);}bool IsRedB()//檢查紅黑樹{if (_root && _root->_col == RED){cout << "根節點顏色是紅色" << endl;return false;}int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}// 連續紅色節點return _Check(_root, 0, benchmark);}int Height(){return _Height(_root);}private:void _Destroy(Node* root)//釋放空間{if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}int _Height(Node* root){if (root == NULL)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;}bool _Check(Node* root, int blackNum, int benchmark)//紅黑樹的驗證{if (root == nullptr)//走到空,就判斷黑色節點數量是否一樣{if (benchmark != blackNum){cout << "某條路徑黑色節點的數量不相等" << endl;return false;}return true;}if (root->_col == BLACK)//重新記錄每條路徑下的黑色節點{++blackNum;}if (root->_col == RED&& root->_parent&& root->_parent->_col == RED)//連續紅色節點也會報錯{cout << "存在連續的紅色節點" << endl;return false;}//每次遞歸都把黑色節點數量傳給下一個棧幀return _Check(root->_left, blackNum, benchmark)&& _Check(root->_right, blackNum, benchmark);}void _InOrder(Node* root)//中序遍歷打印{if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}void RotateL(Node* parent)//左旋{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppnode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}void RotateR(Node* parent)//右旋{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}private:Node* _root = nullptr;
};int main()
{int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };RBTree<int, int> t1;for (auto e : a){t1.Insert(make_pair(e, e));}t1.InOrder();//j檢查打印出來的數據是否有序cout << endl << t1.IsRedB() << endl;return 0;
}

? ? ? ? ?運行結果:

? ? ? ? 從上面代碼可以看出,檢驗一棵樹是否為紅黑樹,從以下幾個方面進行判斷:函數IsRedB的作用是判斷根結點是否為黑色,然后隨便遍歷一條路徑,記錄該路徑下黑色節點的數量(規則5)。?

? ? ? ? 接著把記錄下來的黑色節點數量傳給函數_check,并且讓_check函數把所有路徑下的黑色節點記錄下來,一一的去跟之前記錄好的數據進行對比,若有不相等的情況說明該樹不是紅黑樹,另外_check函數內還進行了紅色節點是否連續的判斷(規則3)。

結語

? ? ? ? 以上就是關于紅黑樹的講解,紅黑樹的重點還是在于了解紅黑樹的調整邏輯,理清叔叔節點和父母節點他們的位置關系,最核心的還是叔叔節點,他的存在與否,是紅色還是黑色都影響最終的調整規律。最后希望本文可以給你帶來更多的收獲,如果本文對你起到了幫助,希望可以動動小指頭幫忙點贊👍+關注😎+收藏👌!如果有遺漏或者有誤的地方歡迎大家在評論區補充,謝謝大家!!

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

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

相關文章

【Mysql】Navicat數據庫勿刪了mysql.infoschema@localhost,導致打不開數據庫,如何修改

運行報錯如下&#xff1a; 1449 . The user specified as a definer (mysql.infoschemaocalhost) does not exist該方法不需要重啟mysql&#xff0c;或者重裝&#xff1b;僅需要恢復刪除的mysql.infoschemalocalhost用戶 一、登錄建立用戶 mysql -uroot -pxxxxxx密碼二、建立…

【網上商城系統的設計與開發】

目錄 1.實訓概況 1 1.1 實訓題目 1 1.2實訓時間 1 1.3實訓目的 1 1.4 實訓環境 1 1.5 實訓內容 2 1.6 進度安排 3 2.需求分析 5 2.1 功能需求分析 5 2.1.1用戶需求分析 5 2.2.2網站前臺需求 5 2.2.3網站后臺需求 6 2.2 可行性分析 7 2.2.1社會可行性 7 2.2.2技術可行性 8 3.系統…

Sora學習(一):Sora技術路徑整體認知

前文&#xff1a;最近跟著DataWhale組隊學習這一期“Sora原理與技術實戰”&#xff0c;本篇博客主要是基于DataWhale成員、廈門大學平潭研究院楊知錚研究員分享的Sora技術原理詳解課件內容以及參考網上一些博客資料整理而來&#xff08;詳見文末參考文獻&#xff09;&#xff0…

【談一談】并發編程_鎖的分類

【談一談】并發編程_鎖的分類 Hello!~大家好!~每天進步一點點,日復一日,我們終將問劍頂峰 這里主要是介紹下我們常用的鎖可以分為幾類,目的是整體框架作用~方便后續的并發文章 說白了,這篇就是開頭哈~ 本文總綱: 一.可重入鎖和不可重入鎖 我們開發中一般用到的都是可重入鎖比如…

Photoshop 2023:重塑創意,引領數字藝術新紀元

在數字藝術的浩瀚星空中&#xff0c;Adobe Photoshop 2023&#xff08;簡稱PS 2023&#xff09;如同一顆璀璨的新星&#xff0c;為Mac和Windows用戶帶來了前所未有的創意體驗。這款強大的圖像處理軟件不僅繼承了前作的精髓&#xff0c;更在細節上進行了諸多創新&#xff0c;讓每…

運行Python文件時出現‘utf-8’code can‘t decode byte 如何解決?(如圖)

如圖 亦或者出現“SyntaxError: Non-UTF-8 code starting with \xbb ” 出現這種問題往往是編碼格式導致的&#xff0c;我們可以在py文件中的第一行加入以下代碼&#xff1a; # codingutf-8或者 # codinggdk優先使用gbk編碼 解釋一下常用的兩種編碼格式&#xff1a; utf-…

朱維群將出席用碳不排碳碳中和頂層科技路線設計開發

演講嘉賓&#xff1a;朱維群 演講題目&#xff1a;“用碳不排碳”碳中和頂層科技路線設計開發 簡介 姓名&#xff1a;朱維群 性別&#xff1a;男 出生日期&#xff1a;1961-09-09 職稱&#xff1a;教授 1998年畢業于大連理工大學精細化工國家重點實驗室精細化工專業&…

什么是B+樹,和B樹有什么不同?

&#x1f449;博主介紹&#xff1a; 博主從事應用安全和大數據領域&#xff0c;有8年研發經驗&#xff0c;5年面試官經驗&#xff0c;Java技術專家&#xff0c;WEB架構師&#xff0c;阿里云專家博主&#xff0c;華為云云享專家&#xff0c;51CTO 專家博主 ?? 個人社區&#x…

Spring Initializer環境問題

1.基于jdk8與本地 環境準備 1)下載jdk8并安裝 2&#xff09;下載maven 3.6.3并解壓放入D盤maven目錄下&#xff0c;去掉外層 設置阿里源 打開settings.xml,在mirrors標簽之內增加&#xff0c;注意粘貼后</id>中的/有可能被刪掉&#xff0c;要自己補上 <mirror>&l…

健身房預約小程序制作詳細步驟解析

如果你是一位健身愛好者&#xff0c;或者是一位健身教練&#xff0c;你一定知道預約健身的痛苦。傳統的預約方式不僅麻煩&#xff0c;而且效率低下。但是&#xff0c;現在&#xff0c;我們可以使用一種神仙工具——喬拓云網&#xff0c;來搭建一個屬于自己的健身預約小程序&…

【VTKExamples::PolyData】第四十三期 PolyDataPointSampler

很高興在雪易的CSDN遇見你 VTK技術愛好者 QQ:870202403 前言 本文分享VTK樣例PolyDataPointSampler,并解析接口vtkPolyDataPointSampler,希望對各位小伙伴有所幫助! 感謝各位小伙伴的點贊+關注,小易會繼續努力分享,一起進步! 你的點贊就是我的動力(^U^)ノ~YO …

如何使用 CrewAI 構建協作型 AI Agents

一、前言 AI Agents 的開發是當前軟件創新領域的熱點。隨著大語言模型 (LLM) 的不斷進步&#xff0c;預計 AI 智能體與現有軟件系統的融合將出現爆發式增長。借助 AI 智能體&#xff0c;我們可以通過一些簡單的語音或手勢命令&#xff0c;就能完成以往需要手動操作應用程序才能…

運維的利器–監控–zabbix–grafana

運維的利器–監控–zabbix–grafana 一、介紹 Grafana 是一個跨平臺的開源的度量分析和可視化工具 , 可以通過將采集的數據查詢然后可視化的展示 。zabbix可以作為數據源&#xff0c;為grafana提供數據&#xff0c;然后grafana將數據以圖表或者其他形式展示出來。zabbix和gra…

基于YOLOv的目標追蹤與無人機前端查看系統開發

一、背景與簡介 隨著無人機技術的快速發展&#xff0c;目標追蹤成為無人機應用中的重要功能之一。YOLOv作為一種高效的目標檢測算法&#xff0c;同樣適用于目標追蹤任務。通過集成YOLOv模型&#xff0c;我們可以構建一個無人機前端查看系統&#xff0c;實現實時目標追蹤和可視化…

零基礎學編程,中文編程工具之進度標尺構件的編程用法

零基礎學編程&#xff0c;中文編程工具之進度標尺構件的編程用法 一、前言 今天給大家分享的中文編程開發語言工具 進度條構件的用法。 編程入門視頻教程鏈接 https://edu.csdn.net/course/detail/39036 編程工具及實例源碼文件下載可以點擊最下方官網卡片——軟件下載——…

機器人持續學習基準LIBERO系列9——數據集軌跡查看

0.前置 機器人持續學習基準LIBERO系列1——基本介紹與安裝測試機器人持續學習基準LIBERO系列2——路徑與基準基本信息機器人持續學習基準LIBERO系列3——相機畫面可視化及單步移動更新機器人持續學習基準LIBERO系列4——robosuite最基本demo機器人持續學習基準LIBERO系列5——…

Python AI 實現繪畫功能(附帶源碼)

本文我們將為大家介紹如何基于一些開源的庫來搭建一套自己的 AI 作圖工具。 需要使用的開源庫為 Stable Diffusion web UI&#xff0c;它是基于 Gradio 庫的 Stable Diffusion 瀏覽器界面 Stable Diffusion web UI GitHub 地址&#xff1a;GitHub - AUTOMATIC1111/stable-dif…

快速解決maven依賴沖突

我們在開發過程中經常出現maven依賴沖突&#xff0c;或者maven版本不匹配的情況&#xff0c;我們可以使用阿里云原生腳手架來做maven管理&#xff0c;添加需要的組件&#xff0c;然后點擊獲取代碼&#xff0c;就可以獲得對應的依賴文件。

【重要公告】對BSV警報系統AS的釋義

??發表時間&#xff1a;2024年2月15日 由BSV區塊鏈協會開發并管理的BSV警報系統&#xff08;Alert System&#xff0c;以下簡稱“AS”&#xff09;是BSV網絡的重要組件。它是一個復雜的系統&#xff0c;主要職能是在BSV區塊鏈網絡內發布信息。這些信息通常與網絡訪問規則NAR相…

c# 任務(Task)介紹

任務&#xff08;Task&#xff09; Task作為C#異步的核心&#xff0c;Task位于System.Threading.Tasks命名空間下。 創建任務的基本原理是使用線程池&#xff0c;也就是說任務最終也是要交給線程去執行的。但是微軟優化了任務的線程池&#xff0c;使線程的控制更加精準和高效…