【數據結構】紅黑樹——領略天才的想法

個人主頁:東洛的克萊斯韋克-CSDN博客??

祝福語:愿你擁抱自由的風? ? ? ?

目錄

二叉搜索樹

AVL樹

紅黑樹概述

性質詳解

效率對比

旋轉操作

元素操作

代碼實現


二叉搜索樹

【數據結構】二叉搜索樹-CSDN博客

AVL樹

【數據結構】AVL樹——平衡二叉搜索樹-CSDN博客

紅黑樹概述

概念

在二叉搜索樹的基礎上符合一下性質便是紅黑樹

每一個節點不是紅色就是黑色

根節點一定是黑色

空節點一定是黑色

父親節點和孩子節點不能是連續的紅色節點

每一條根節點至空節點路徑上的黑色節點的數量一定相等

?

性質詳解

理解路徑

紅黑樹中,一條完整路徑不是從葉子節點溯源至根節點,而是從空節點溯源至根節點

?

理解最長和最短路徑

如果全是黑色節點,紅黑樹就是一顆滿二叉樹,因為每條路徑的黑色節點數量相等

?

那么這顆樹的每條完整路徑都是最短路徑,

如果在一條路徑上紅黑節點間隔(不允許連續的有紅色節點),那么該路徑為最長路徑。

?

紅黑樹規則

那么最長路徑是最短路徑的兩倍。這便是紅黑樹的平衡規則。

滿二叉樹的平衡條件是左右子樹高度差為0,AVL樹的平衡條件是左右子樹高度差小于等于 1,相比于前兩棵樹平衡條件,紅黑樹是一種弱平衡

紅黑樹和AVL樹一樣,只要保證自己的平衡規則不被打破,就能使自己不退化為類似于鏈表的結構。

退化成類似鏈表的結構——插入的數據接近有序或插入大量的數據不夠隨機或進行了一些插入刪除等操作。

效率對比

查找效率

從直覺上講,紅黑樹只是維持一種弱平衡,在最壞情況下,紅黑樹的高度是AVL樹高度的兩倍,那么紅黑樹查找數據的效率也應該比AVL樹低兩倍才對,為什么我們認為紅黑樹是一種更優的數據結構呢?下面小編帶大家算筆賬

2的40次方是一萬億,也就是說用滿二叉樹存一萬億個數據高度為40。AVL樹是有高度差的,所以最壞情況下會查找40多次,而紅黑樹最壞情況下會找80多次。

那么對于cpu而言,找40多次和找80多次有區別嗎?答案是沒有的,現在的cpu每秒鐘可以運算十億次甚至幾百億。

可以理解為,在查找數據的效率上AVL樹和紅黑樹是一樣的。那么,紅黑樹的優勢在哪里呢?

插入刪除效率

不管是紅黑樹還是AVL樹,如果打破平衡都需要旋轉這一操作恢復平衡,旋轉所付出的時間復雜度為O(1)。對于AVL樹而言,需要溯源更新平衡因子,對于紅黑樹而言,需要溯源更新節點顏色,溯源更新最壞情況下是從葉子節點更新到根節點,所付出的時間復雜度為O(logN)

因為AVL樹的高度差小于等于1,平衡很容易被打破,要維持平衡就需要不斷地付出O(1)O(logN)來維持平衡。

那么紅黑樹維持弱平衡就不需要總是付出這樣地代價,所以紅黑樹是一種更優的數據結構

旋轉操作

旋轉操作不是AVL樹或紅黑樹特有的,旋轉一次的本質是讓二叉搜索樹的某棵子樹的高度減一

對于紅黑樹而言,最長路徑是最短路徑的二倍加一,就意味著打破平衡,需要通過旋轉讓最長路徑上的某棵子樹高度減一來恢復平衡。旋轉后需要更新節點的顏色,具體要怎么控制顏色下面細講,現在看一下旋轉操作吧

左單旋:新節點插入較高右子樹的右側——對fathernode

?

void RevolveLeft(node *& fathernode)//左單旋      
{node* cur = fathernode->_right; //父親節點的右孩子fathernode->_right = cur->_left; //更改指向關系if (cur->_left != nullptr) //特殊情況cur->_left->_FatherNode = fathernode;//更改指向關系cur->_FatherNode = fathernode->_FatherNode;//更改指向關系if (fathernode->_FatherNode != nullptr) //為空是特殊情況,{if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;//更改指向關系}else{fathernode->_FatherNode->_left = cur;//更改指向關系}}cur->_left = fathernode;//更改指向關系fathernode->_FatherNode = cur;//更改指向關系}

右單旋:新節點插入較高左子樹的左側——對fathernode

void RevolveRight(node *& fathernode) //右單旋
{node* cur = fathernode->_left; //父親節點的左節點fathernode->_left = cur->_right;//更新指向關系if (cur->_right != nullptr) //特殊情況cur->_right->_FatherNode = fathernode;//更新指向關系cur->_FatherNode = fathernode->_FatherNode;//更新指向關系if (fathernode->_FatherNode != nullptr)//特殊情況{if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;//更新指向關系}else{fathernode->_FatherNode->_left = cur;//更新指向關系}}cur->_right = fathernode;//更新指向關系fathernode->_FatherNode = cur;//更新指向關系}

?

左右雙旋:新節點插入在較高左子樹的右側——先對fathernodeL左單旋再對fathernodeLR右單旋

?

右左雙旋:新節點插入再較高右子樹的左側——先對fathernodeR右單旋再對fathernodeRL左單旋

?

元素操作

我們再來理解一下紅黑樹兩條核心性質

父親節點和孩子節點不能是連續的紅色節點

每一條根節點至空節點路徑上的黑色節點的數量一定相等

紅黑樹插入的新節點應該是黑色還是紅色呢?

也就是說,插入紅色節點可能會破壞第一條性質,插入黑色節點會破壞第二條性質。第一條性質被破壞只影響了當前路徑,而第二條性質被破壞影響的是所有路徑。所以插入新節點的顏色是紅色

如果新插入節點的父親節點是黑色,那么不會破壞任何性質,如果新插入節點的父親節點是紅色該怎么辦呢?

顏色管理

下面介紹紅黑樹如何通過管理顏色來判斷是否需要旋轉,為了方便討論討論,給以下節點起個別名,

父親節點:Father

孩子節點:child(父親節點的孩子節點)

祖父節點:grandfather(父親節點的父親節點)

叔叔節點:uncle(如果父親節點是祖父節點的左孩子,叔叔節點就是祖父節點的右孩子,反之亦然)

如果新節點的父親節點為黑,插入成功。如果父親節點為紅,需要溯源更新顏色,規則如下:

如果uncle存在且為紅色,Father和uncle變為黑色,grandfather變為黑色,讓grandfather變為child,繼續溯源更新

如果更新至整棵樹的根節點(Father為空),讓根節點置黑,或uncle為空或uncle黑色,停止溯源更新(uncle為空或uncle黑色后面會討論)。

如果uncle不存在,或uncle存在且為黑,說明紅黑樹的平衡被打破了,此時就不需要溯源更新顏色了,需要旋轉來恢復紅黑樹的平衡,旋轉之后再更新Father,grandfather或child的顏色

右單旋顏色更新:

Father成為了根需要變成黑色節點,Father旋轉之前的孩子節點為黑,該孩子會成為grandfather左孩子,grandfather需要變成紅節點。

為什么Father旋轉之前的孩子節點為黑呢?因為數據是一個一個插入的,新節點只會和一條路徑上的父親節點沖突,如果這是一顆正常的紅黑樹,Father旋轉之前的孩子節點只能為黑色。Father作為根是黑色的,Father的孩子節點也只能是紅色的。下面的旋轉也一樣

左單旋顏色更新:

還是和右單旋一樣,Father成了根需要變成黑色,grandfather需要變成紅色。

雙旋顏色更新:

無論是左右雙旋還是右左雙旋,最終都是child變成了根,grandfather和Father變成了child的左右孩子,grandfatjie作為孩子需要變成紅色。

代碼實現

【C++】詳解C++的模板-CSDN博客

enum Color { RED, BLACK };    template <class K>
class RBTreeNode
{
public:typedef RBTreeNode <K> node_type;   K _ky;   node_type* _left;node_type* _right;node_type* _FatherNode;  Color _color;RBTreeNode(const K& key):_ky(key),_left(nullptr),_right(nullptr), _FatherNode(nullptr)  ,_color(RED){}};template <class K> 
class RBTree
{
public:typedef RBTreeNode <K> node_type;RBTree():_root(nullptr)    {}bool Insert(const K& key)//插入元素     {////if (nullptr == _root) //是否是空樹{_root = new node_type(key);_root->_color = BLACK;     return true;}//node_type* cur = _root;node_type* fathernode = nullptr;  while (cur){if (cur->_ky < key)    {fathernode = cur;  cur = cur->_right;}else if (cur->_ky > key)     {fathernode = cur;cur = cur->_left;}else{return false;}}cur = new node_type(key); //新插入節點 if (fathernode->_ky < cur->_ky)    {fathernode->_right = cur;cur->_FatherNode = fathernode;}else{fathernode->_left = cur;cur->_FatherNode = fathernode;}while (fathernode && fathernode->_color == RED)  {node_type* grandfather_node = fathernode->_FatherNode;node_type* unclenode = nullptr;if (fathernode == grandfather_node->_left) //父親節點是祖父節點的左孩子{unclenode = grandfather_node->_right; //叔叔節點是祖父節點的右孩子if (unclenode == nullptr || unclenode->_color == BLACK){if (cur == fathernode->_left)  {RevolveRight(fathernode);fathernode->_color = BLACK;grandfather_node->_color = RED; }else if (cur == fathernode->_right){RevolveLeft(fathernode);RevolveRight(cur);cur->_color = BLACK;   grandfather_node->_color = RED;     }}else{fathernode->_color = BLACK; //父親變黑unclenode->_color = BLACK;  //叔叔變黑grandfather_node->_color = RED; //爺爺變紅cur = grandfather_node;fathernode = cur->_FatherNode; }}else//父親節點是祖父節點的右孩子{unclenode = grandfather_node->_left; //叔叔節點是祖父節點的左孩子       if (unclenode == nullptr || unclenode->_color == BLACK){if (cur == fathernode->_right){RevolveLeft(fathernode);   fathernode->_color = BLACK;grandfather_node->_color = RED;}else if (cur == fathernode->_left){RevolveRight(fathernode);RevolveLeft(cur);     cur->_color = BLACK;grandfather_node->_color = RED;}}else  {fathernode->_color = BLACK; //父親變黑unclenode->_color = BLACK;  //叔叔變黑grandfather_node->_color = RED; //爺爺變紅cur = grandfather_node;fathernode = cur->_FatherNode;}}}_root->_color = BLACK;  return true;}private:void RevolveLeft(node_type*& fathernode)//左單旋        {node_type* cur = fathernode->_right;  fathernode->_right = cur->_left;if (cur->_left != nullptr)cur->_left->_FatherNode = fathernode;cur->_FatherNode = fathernode->_FatherNode;if (fathernode->_FatherNode != nullptr){if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;}else{fathernode->_FatherNode->_left = cur;}}cur->_left = fathernode;fathernode->_FatherNode = cur;}void RevolveRight(node_type*& fathernode) //右單旋 {node_type* cur = fathernode->_left;  fathernode->_left = cur->_right;if (cur->_right != nullptr)cur->_right->_FatherNode = fathernode;cur->_FatherNode = fathernode->_FatherNode;if (fathernode->_FatherNode != nullptr){if (fathernode->_FatherNode->_right == fathernode){fathernode->_FatherNode->_right = cur;}else{fathernode->_FatherNode->_left = cur;}}cur->_right = fathernode;fathernode->_FatherNode = cur;}node_type* _root;
};

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

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

相關文章

深度學習實戰-yolox訓練ExDark數據集(附全過程代碼,超詳細教程,無坑!)

跳轉:數據集獲取以及前期準備工作 本人在深度學習實戰-yolov5訓練ExDark數據集(附全過程代碼,超詳細教程,無坑!)的數據基礎上實現yolox的訓練,所以先跳轉到該文章下去獲取數據集,再繼續接下來操作過程。 一、VOC格式數據集制作 1.前期工作 2.轉變成voc格式 在datase…

Latex:newcommand

參考文獻&#xff1a; latex中自定義的命令———\newcommand-CSDN博客LaTeX技巧924&#xff1a;詳解newcommand的參數和默認值 - LaTeX工作室 (latexstudio.net) 文章目錄 (re)newcommand自定義的一些命令 (re)newcommand ”定義命令“ 的定義&#xff1a; \newcommand{<…

[每周一更]-(第98期):PHP版本的升級歷程

文章目錄 大致歷程PHP/FI (PHP 1.0)PHP 2.0PHP 3.0PHP 4.0PHP 5.0PHP 5.3 - 5.6PHP 7.0PHP 7.1 - 7.4PHP 8.0PHP 8.1 - 8.2 參考 PHP&#xff0c;即“超文本預處理器”&#xff08;Hypertext Preprocessor&#xff09;&#xff0c;是廣泛應用于web開發的服務器端腳本語言。自19…

什么是獨特擺動交易策略?fpmarkets1分鐘講清楚

擺動交易策略想必各位投資者都已經接觸過了&#xff0c;但是什么是獨特擺動交易策略&#xff1f;各位投資者知道嗎&#xff1f;其實很簡單&#xff0c;這是一種基于斐波納契工具的獨特擺動交易策略。下面fpmarkets1分鐘講清楚&#xff0c;趨勢總會經歷調整&#xff0c;而這些調…

【機器學習】Python中的決策樹算法探索

&#x1f308;個人主頁: 鑫寶Code &#x1f525;熱門專欄: 閑話雜談&#xff5c; 炫酷HTML | JavaScript基礎 ?&#x1f4ab;個人格言: "如無必要&#xff0c;勿增實體" 文章目錄 Python中的決策樹算法探索引言1. 決策樹基礎理論1.1 算法概述1.2 構建過程 2. P…

數據集003:貓類識別-12種貓分類數據集 (含數據集下載鏈接)

數據集簡介&#xff1a; 訓練集共有2160張貓的圖片, 分為12類. train_list.txt是其標注文件 測試集共有240張貓的圖片. 不含標注信息. 訓練集圖像&#xff08;部分&#xff09; 驗證集圖像&#xff08;部分&#xff09; 標簽 部分代碼&#xff1a; # 定義訓練數據集 class T…

eNSP華為模擬器-DHCP配置

拓撲圖 要求 PC1通過DHCP獲取192.168.1.1地址PC2和PC3通過DHCP接口地址池方式獲取IP地址配置靜態路由使其ping通 配置 配置主機名及接口IP地址 # AR1 <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sys AR1 [AR1]int g0/0/0 [AR1-Gigabit…

在leaflet上創建圖標

參考之前博客進行創建leaflet地圖 添加圖標 customIcon L.icon({iconUrl: helicopter0.png,//圖片路徑放在public中iconSize: [35, 35],iconAnchor: [15, 15],tooltipAnchor: [20, 0],}); let marker L.marker([obj.lat, obj.lon], { icon: customIcon, rotationAngle: 偏轉…

去重復記錄和排序——kettle開發09

一、去除重復記錄 去除重復記錄&#xff0c;就是將數據流中的數據進行字段比較&#xff0c;從而去掉重復值的過程。去除重復記錄的前提是需要將數據流中的數據進行排序&#xff0c;然后再進行去重操作。 去除重復記錄的邏輯是&#xff0c;如下圖&#xff0c;我們將需要比較的…

MySQL + MyBatis-Plus 分頁數據重復問題

參考文章&#xff1a;java - MySQL MyBatis-Plus 分頁數據重復問題 - 個人文章 - SegmentFault 思否

基礎使用-SQL-圖形化界面工具DataGrip

一、連接mysql &#xff08;1&#xff09;選擇加號&#xff0c;再選擇添加一個數據源&#xff08;Data Source&#xff09;&#xff0c;然后選擇MySQL &#xff08;2&#xff09;接下來就需要去配置MySQL的連接信息&#xff0c;并且去下載它的驅動&#xff0c;安裝驅動時可能要…

微信公眾號怎么做留言板功能

在繁忙的都市生活中&#xff0c;你是否常常感到孤單、渴望有一個可以傾訴心聲的地方&#xff1f;今天&#xff0c;我要為大家介紹一個特別的角落——我們公眾號的留言板功能。它不僅是一個留言板&#xff0c;更是一個情感交流的平臺&#xff0c;一個可以讓我們彼此心靈相通的橋…

百度發布代碼輔助工具,超強

不會用AI的程序員&#xff0c;會跟不會用智能手機的人一樣 百度這個代碼助手助手感覺還是不錯的 https://comate.baidu.com/?inviteCodeijmce7dj 目前看下來這個代碼助手是比較強的&#xff0c;比阿里的那個靈碼好用&#xff0c;他可以引用到當前的文件&#xff0c;并且能分…

安裝electron報鏡像源錯誤

報錯信息如下&#xff1a; F:\xxxxxx>npm i npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion1, but package-lock.json was generated for lockfileVersion2. Ill try to do my best with it!> vue-demi0.14.6 postinstall F…

idea改了代碼,但是需要緊急切換分支,需要把改動的保存到本地

但是如果有沖突&#xff0c;你沒有合并&#xff0c;那也會丟哦&#xff01; 改完那個分支&#xff0c;回到這個分支然后彈出來再。

Delphi 程序例子(DPI變化自動感知及顯示器相關功能演示)

目錄 一、前言 二、Delphi 演示程序&#xff08;D12版本&#xff0c;用D11也都可以&#xff09; 1. 演示程序功能&#xff1a; 2. 程序界面&#xff1a; 3. 程序源代碼下載&#xff08;有償&#xff09;&#xff1a; 一、前言 系列文章&#xff1a; 徹底搞懂 Windows 顯示…

YOLOv5 | 卷積模塊 | 提高網絡的靈活性和表征能力的動態卷積【附代碼+小白可上手】

&#x1f4a1;&#x1f4a1;&#x1f4a1;本專欄所有程序均經過測試&#xff0c;可成功執行&#x1f4a1;&#x1f4a1;&#x1f4a1; 輕量級卷積神經網絡由于其低計算預算限制了CNNs的深度&#xff08;卷積層數&#xff09;和寬度&#xff08;通道數&#xff09;&#xff0c;…

三分鐘一條AI小和尚視頻 ,日引300+創業粉。單日變現四位數 全套工具

經過六個月的不懈努力和無數次的嘗試錯誤&#xff0c;我終于找到了一個高效引流和積累粉絲的新策略&#xff0c;并愿意與大家無私分享。這一次&#xff0c;我將詳盡地介紹這個方法&#xff0c;建議朋友們多次觀看以徹底掌握其精髓。 簡而言之&#xff0c;該策略主要依托于AI繪…

C語言文件編程

C語言文件編程 第一部分 基本概念 1、Linux文件類型 1.-普通文件&#xff1a;存在于外部存儲器中&#xff0c;用于存儲普通數據。 1.txt 1.c 1.mp3 1.mp4 2.d目錄文件&#xff1a;用于存放目錄項&#xff0c;是文件系統管理的重要文件類型。 文件夾 3.p管道文件&#x…

基于springboot+vue的“漫畫之家”系統

開發語言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服務器&#xff1a;tomcat7數據庫&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;數據庫工具&#xff1a;Navicat11開發軟件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…