從零開始的C++(十九)

紅黑樹:

一種接近平衡的二叉樹,平衡程度低于搜索二叉樹。

特點:

1.根節點為黑

2.黑色結點的子結點可以是紅色結點或黑色結點。

3.紅色結點的子結點只能是黑色結點。

4.每個結點到其所有葉子結點的路徑的黑色結點個數相同。

5.指向空的結點,指向空的那一側視為指向黑色結點(指向nullptr的黑色結點,用于明確路徑)

6.由于上述規則,使得紅黑樹任意結點的左右子樹高度差不超過二倍(最小是全黑,最大是紅黑相交,又由于黑色結點個數需要一致,因此高度不會超過二倍)。

模擬實現紅黑樹:

1.紅黑樹的插入:

	// 注意:為了簡單起見,本次實現紅黑樹不存儲重復性元素bool Insert(const T& data){if (_pHead == nullptr){Node* cur = new Node(data);_pHead = cur;_pHead->_color = black;return true;}//有根節點的時候Node* cur = new Node(data);Node* news = _pHead;Node* parent = nullptr;//查找插入位置while (news != nullptr){if (news->_data < data){   parent = news;news = news->_pRight;}else if (news->_data > data){ parent = news;news = news->_pLeft;}else{return false;}}//確定插入位置是parent的左還是右if (parent->_data < data){parent->_pRight = cur;cur->_pParent = parent;}else{parent->_pLeft = cur;cur->_pParent = parent;}//開始向上判斷//若父節點為黑則可以正常插入,無任何影響//父節點為紅才進入,若父節點為空則cur指向根結點同樣退出while (parent&&parent->_color==red){   Node* grandparent = parent->_pParent;//parent為紅則一定有父節點if(grandparent->_color==black){//得到uncleNode* uncle = nullptr;if (parent->_data < grandparent->_data){uncle = grandparent->_pRight;}else{uncle = grandparent->_pLeft;}//判斷uncle情況if (uncle && uncle->_color == red){  //UNCLE存在且是紅parent->_color = uncle->_color = black;grandparent->_color = red;cur = grandparent;parent = cur->_pParent;}else{//uncle為黑或者uncle為空if (cur == parent->_pLeft&& parent == grandparent->_pLeft){//右旋RotateR(grandparent);//更新顏色parent->_color = black;grandparent->_color = red;}else if (cur == parent->_pRight&& parent == grandparent->_pRight){//左旋RotateL(grandparent);//更新顏色parent->_color = black;grandparent->_color = red;}//更新后當前子樹的根節點為黑,無需向上繼續判斷,因此parent->_color可以直接使得退出循環else if(cur == parent->_pRight&& parent == grandparent->_pLeft){//先左旋parent再右旋grandparentRotateL(parent);RotateR(grandparent);cur->_color = black;grandparent->_color = red;break;}else{RotateR(parent);RotateL(grandparent);cur->_color = black;grandparent->_color = red;break;}}}else{	 //此時grandparent和parent顏色均是紅,報錯cout << parent->_data<<"出現連續紅結點" << endl;assert(false);}}_pHead->_color = black;}// 左單旋void RotateL(Node* pParent){Node* parent = pParent;Node* pr = parent->_pRight;Node* prl = pr->_pLeft;//記錄父節點Node* pp = parent->_pParent;//更新指針parent->_pRight = prl;if (prl)//判斷prl是否存在prl->_pParent = parent;pr->_pLeft = parent;parent->_pParent = pr;if (pp == nullptr){//若parent是根節點_pHead = pr;pr->_pParent = nullptr;}else{   //父節點連接if (pp->_data < parent->_data){pp->_pRight = pr;pr->_pParent = pp;}else{pp->_pLeft = pr;pr->_pParent = pp;}}}// 右單旋void RotateR(Node* pParent){Node* parent = pParent;Node* pl = parent->_pLeft;Node* plr = pl->_pRight;//記錄父節點Node* pp = parent->_pParent;//更新指針parent->_pLeft = plr;if (plr)//判斷prl是否存在plr->_pParent = parent;pl->_pRight = parent;parent->_pParent = pl;if (pp == nullptr){//若parent是根節點_pHead = pl;pl->_pParent = nullptr;}else{   //父節點連接if (pp->_data < parent->_data){pp->_pRight = pl;pl->_pParent = pp;}else{pp->_pLeft = pl;pl->_pParent = pp;}}}

插入一個結點,初始時插入結點為紅(若為黑則會導致其祖先結點每條路徑黑色結點的個數改變,修改十分麻煩)。若此時父節點為黑,則無需進行任何其余操作。

若父節點為紅,此時會出現連續紅色結點的情況,不符合紅黑樹的定義,因此需要修改。

修改主要分以下幾種情況:

1.

此時叔叔結點存在且為紅色。

修改方式是將父節點和叔叔結點改成黑色,祖先結點改成紅色,然后將祖先結點視為新插入結點繼續向上判斷。此時父節點和叔叔結點改成黑色,不會受其子節點顏色的影響,且對于祖先結點的父節點,每條路徑的黑色結點的個數沒有變化,因此仍符合紅黑樹。

2.

此時叔叔結點為黑色結點或者不存在(不存在的情況,根據紅黑樹規則指向空的結點,指向空的那一側視為指向黑色結點,因此也可以看做叔叔結點為黑色結點)。此時需要進行旋轉來修改這棵子樹。若祖先結點和父節點以及新插入結點都在同一側(比如父節點是祖先結點左子樹,新插入結點是父節點的左子樹),則只需要對祖先結點進行一次旋轉,旋轉完成后將父節點改成黑色,祖先結點改成紅色即可。若不在同一側,則需要先對父節點進行一次旋轉,再對祖先結點進行一次旋轉,旋轉完成后新插入結點的顏色為黑,祖先結點顏色為紅。

在同一側的旋轉一次。


不在同一側,旋轉兩次。

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

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

相關文章

OmniGraffle

安裝 在mac上安裝OmniGraffle&#xff0c;找一個正版或者啥的都行&#xff0c;安裝好后&#xff0c;可以直接在網上找一個激活碼&#xff0c;然后找到軟件的許可證&#xff0c;進行添加即可。 使用 新建空白頁 然后圖形啥的看一眼工具欄就知道了&#xff0c;顏色形狀還是挺…

音視頻項目—基于FFmpeg和SDL的音視頻播放器解析(二十一)

介紹 在本系列&#xff0c;我打算花大篇幅講解我的 gitee 項目音視頻播放器&#xff0c;在這個項目&#xff0c;您可以學到音視頻解封裝&#xff0c;解碼&#xff0c;SDL渲染相關的知識。您對源代碼感興趣的話&#xff0c;請查看基于FFmpeg和SDL的音視頻播放器 如果您不理解本…

【C++】拷貝構造函數,析構函數詳解!

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;個人主頁 &#xff1a;阿然成長日記 …

【LeetCode】挑戰100天 Day13(熱題+面試經典150題)

【LeetCode】挑戰100天 Day13&#xff08;熱題面試經典150題&#xff09; 一、LeetCode介紹二、LeetCode 熱題 HOT 100-152.1 題目2.2 題解 三、面試經典 150 題-153.1 題目3.2 題解 一、LeetCode介紹 LeetCode是一個在線編程網站&#xff0c;提供各種算法和數據結構的題目&…

Vue3 實現elementPlus的table列寬調整和拖拽

1、需要的包 // 除了Vue和element-plus外還需要以下的包 npm install sortablejs2、具體代碼如下&#xff0c;可直接粘貼運行 <template><div class"draggable-table"><el-table ref"tableRef":data"tableData.data":key"…

Java-飛翔的小鳥

前言 基于Java的飛翔小鳥游戲&#xff0c;本代碼來自b站up主分享。本游戲所需的圖片素材需要自己獲取并下載&#xff0c;在此視頻下&#xff0c;視頻鏈接&#xff1a;【Java經典小游戲項目之飛翔的小鳥】 https://www.bilibili.com/video/BV1ou411o7br/?p10&share_source…

C#編程題分享(4)

換行輸出整數問題 輸?任意?個位數未知的整數&#xff0c;輸出這個數每?位上的數字。輸出的時候&#xff0c;從個位開始輸出&#xff0c;每輸出?個數字換??。樣例輸?&#xff1a;3547 輸出&#xff1a;7 換行輸出 4 換行輸出5 換行輸出3 int n Convert.ToInt32(Conso…

【python基礎(九)】文件和異常詳解:使用、讀取、寫入、追加、保存用戶的信息,以及優雅的處理異常

文章目錄 一. 從文件中讀取數據1. 讀取整個文件2. 文件路徑3. 逐行讀取4. 創建一個包含文件各行內容的列表 二. 寫入文件1. 寫入空文件2. 寫入多行3. 附加到文件 三. 異常1. 處理ZeroDivisionError異常2. 使用try-except代碼塊3. try-except-else ing4. 處理FileNotFoundError異…

如何在AD上創建完整的項目

首先&#xff0c;我們先安裝好AD&#xff0c;這里我使用的是AD22&#xff0c;安裝過程如下&#xff1a; Altium Designer 22下載安裝教程-CSDN博客 Altium Designer 22是全球領先的PCB設計軟件之一&#xff0c;為電路板設計師提供了一種集成的解決方案&#xff0c;旨在簡化和加…

探討工業元宇宙和數字孿生的關系

就在各類技術專家還在試圖設想元宇宙虛擬世界將為企業和消費者帶來什么時&#xff0c;工業元宇宙虛擬世界已經在改變人們設計、制造以及與各行業物理實體互動的方式。盡管元宇宙的定義比比皆是&#xff0c;工業元宇宙將如何發展還有待觀察&#xff0c;但數字孿生越來越多地被視…

shell(函數和數組)

目錄 一、函數 1.函數的由來 2.函數的作用 3.函數的使用方法 4.函數的定義 5.查看函數 6.刪除函數 7.函數返回值 8.函數的傳參數 9.函數遞歸 二、數組 1.數組的相關介紹 2.聲明數組 3.定義數組的格式 4.冒泡排序 總結&#xff1a;本章主要介紹了函數和數組相關知…

運維 在Windows上搭建小型Git服務

文章目錄 1、Git選型1.1、主要特性1.2、代碼管理1.3、工單管理1.4、Pull/Merge requests1.5、第三方集成1.6、選型結論 2、環境搭建2.1、Gitea下載2.2、Gitea安裝2.3、配置服務信息2.4、運行服務2.5、注冊Gitea為服務2.6、正常使用 1、Git選型 1.1、主要特性 1.2、代碼管理 1.…

多數據庫使用django-apscheduler時,migrate后并不能生成django_apscheduler_djangojob表的問題

先說一下django-apscheduler定時器的使用過程&#xff1a; django-apscheduler基本使用 1.安裝django-apscheduler代碼如下&#xff08;示例&#xff09;&#xff1a; pip install django-apscheduler 2.配置settings.py的INSTALLED_APPS代碼如下&#xff08;示例&#xff09…

項目中常用的 19 條 SQL 優化寶典

一、EXPLAIN 做MySQL優化,我們要善用 EXPLAIN 查看SQL執行計劃。 下面來個簡單的示例,標注(1,2,3,4,5)我們要重點關注的數據 type列,連接類型。一個好的sql語句至少要達到range級別。杜絕出現all級別 key列,使用到的索引名。如果沒有選擇索引,值是NULL。可以采取強制索引…

【CCF-PTA】第03屆Scratch第01題 -- 夢醒時分

夢醒時分 【題目描述】 睡眠是人體正常的生理需要&#xff0c;同年齡男女睡眠時間無明顯差別&#xff0c;一般是8小時左右。居家的小明作息生活很規律&#xff0c;晚上11點睡覺&#xff0c;早晨7點起床學習。請你編寫程序來判斷&#xff0c;每周&#xff08;共168小時&#x…

【JavaEE初階】 JavaScript相應的WebAPI

文章目錄 &#x1f332;WebAPI 背景知識&#x1f6a9;什么是 WebAPI&#x1f6a9;什么是 API &#x1f38d;DOM 基本概念&#x1f6a9;什么是 DOM&#x1f6a9;DOM 樹 &#x1f340;獲取元素&#x1f6a9;querySelector&#x1f6a9;querySelectorAll &#x1f384;事件初識&am…

不是吧?線程池這樣搞?

其他系列文章目錄 設計模式合集 多線程合集 分布式合集 ES合集 文章目錄 系列文章目錄 前言 一、為什么需要線程池&#xff1f; 二、舉個背景例子 三、怎么創建線程池&#xff1f; 四、線程池指定線程數 前言 學習線程池能夠幫助我們更好地處理多線程編程&#xff0c;并提高程…

TikTok美區本土店鋪如何做好IP隔離?

為什么要進行IP隔離呢&#xff1f;因為我們無法在國內直接運營Shopee、TikTok、Lazada等平臺的本土店&#xff0c;平臺識別出店鋪登錄IP非本土IP&#xff0c;則容易導致店鋪風控、被標記為偽本土店&#xff0c;影響店鋪經營。 TikTok美區店鋪的IP隔離方法和Shopee本土店一致&a…

SpringMVC(二)

八、HttpMessageConverter HttpMessageConverter&#xff0c;報文信息轉換器&#xff0c;將請求報文轉換為Java對象&#xff0c;或將Java對象轉換為響應報文 HttpMessageConverter提供了兩個注解和兩個類型&#xff1a;RequestBody&#xff0c;ResponseBody&#xff0c;Reque…

【MySQL】子查詢

文章目錄 子查詢IN運算符子查詢 VS 連接ALL關鍵字ANY關鍵字相關子查詢 !EXISTS運算符select子句中的子查詢from子句中的子查詢 子查詢 獲取價格大于id為3的貨物的商品 用到了內查詢&#xff0c;獲取id為3的商品的單價&#xff0c;把結構傳給外查詢 在where子句中編寫子查詢&am…