進階數據結構:紅黑樹

嘿,各位技術潮人!好久不見甚是想念。生活就像一場奇妙冒險,而編程就是那把超酷的萬能鑰匙。此刻,陽光灑在鍵盤上,靈感在指尖跳躍,讓我們拋開一切束縛,給平淡日子加點料,注入滿滿的passion。準備好和我一起沖進代碼的奇幻宇宙了嗎?Let's go!

我的博客:yuanManGan

我的專欄:C++入門小館?C言雅韻集?數據結構漫游記? 閑言碎語小記坊?題山采玉?領略算法真諦

紅黑樹的介紹

紅黑樹也是一棵平衡二叉樹,我們之前實現的AVL樹,他是通過多次的旋轉而得到的平衡,付出了相應的代價,讓平衡因子的絕對值小于2,才使我們二叉樹的高度實現了log n,而我們的紅黑樹要求最長路徑不超過最短路徑的兩倍,通過一下條件來實現:

1.每個節點不是紅色就是黑色,

2.根節點是黑色的

3.每條路徑的黑色節點數相同

4.不存在連續的紅色節點

我們就通過這些條件來實現最長路徑不超過最短路徑的兩倍

注意我們的完整的一條路徑是到左右子樹為空的路徑

像這樣的一棵樹他的路徑數就是9條。

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

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

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

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

紅?樹的效率:

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

h ≈ logN 也就是意味著紅?樹增刪查改最壞也就是?最?路徑2 ? logN ,那么時間復雜度還是 O(logN)。

紅?樹的結構

#pragma once
enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _parent;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _left;Colour _col;RBTreeNode(const pair<K, V>& kv): _kv(kv), _right(nullptr), _left(nullptr), _col(BLACK){}
};
template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;public:
private:Node* _root = nullptr;
};

紅?樹的插?

我們的插入過程的前半部分和二叉搜索樹的過程基本一樣,先找到插入的位置插入后,進行平衡調整。

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

我們再來想想如果我們要插入一個節點,我們該插入紅色節點還是黑色節點呢?我們要保證每個路徑的黑色節點數相同,如果我們插入黑色節點的話我們就得讓別的路徑的黑色節點都加一才行,所以我們最好插入紅色節點,如果我們插入的節點的位置的父節點是黑色的就萬事大吉,不用進行操作了。

那遇到了父親為紅色節點時我們就得進行變色操作了,

情況1:

p為紅,那么因為p為紅那么g就不可能為紅,這里只有u是未知的,假如u為紅時,我們就得將p和u都變成黑色,然后讓g變成紅,然后繼續向上更新答案,直到更新為parent節點為空或者為黑的時候就結束。

這是我們這種情況的抽象圖:

?情況二:單旋+變?

當我們往一個紅色節點的左邊插入節點時,此時g的右節點為空或者時黑色,我們不能像剛剛那樣操作了,如果我們像那樣操作,我們沒有u節點,然后操作后g的右支路的黑色節點數會多一個我們就需要將g節點進行右旋操作,讓p節點成為c和g節點的根節點。

情況三:雙旋加變色:

我們往這個樹的p位置的右孩子插入一個節點時就變成了

我們得先對p節點進行右旋操作就成了:

?

這個樣子就比較熟悉了,我們再右旋g變個色就解決了。

?

同樣的思路,當u存在且為黑時也是一樣的解決方法。

插入完整的代碼如下:

	bool Insert(const pair<K, V>& kv){if (_root == nullptr)  // 新增:處理根節點為空{_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;//插入操作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;}elseparent->_left = cur;cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//1.u存在且為紅if (uncle && uncle->_col == RED){//變色parent->_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;}else{//     g//   p   u//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else // (parent == grandfather->_right){Node* uncle = grandfather->_left;//1.u存在且為紅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;}

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

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

相關文章

如何上傳github(解決git的時候輸入正確的賬號密碼,但提示認證失敗)

如何上傳github文件&#xff0c;刪除文件 1.重點 GitHub 從 2021 年 8 月 13 日起移除了對密碼認證的支持。你需要使用個人訪問令牌(Personal Access Token, PAT)或 SSH 密鑰來進行認證。 2.生成SSH key 進入設置點擊New SSH Key名字隨便取&#xff0c;可以自己方便記3.上傳文件…

多級緩存架構與熱點探測系統核心技術解析

多級緩存架構與熱點探測系統核心技術解析 &#x1f4cc; 一、多級緩存架構 1. 為什么需要多級緩存&#xff1f; ? 本地緩存優勢&#xff1a; &#x1f680; 減少網絡請求&#xff0c;提升訪問性能&#x1f310; 分布式系統中天然具有分布式緩存特性?? 有效降低遠程緩存&…

iOS 性能監控工具全解析 選擇合適的調試方案提升 App 性能

在iOS應用開發中&#xff0c;性能往往是決定用戶體驗的關鍵因素之一。用戶體驗的優劣&#xff0c;不僅取決于功能的實現&#xff0c;還在于流暢度、響應速度、資源消耗等方面的表現。因此&#xff0c;性能監控工具在iOS開發中的重要性不可小覷。 無論是提升應用的啟動時間、減少…

C++ :vector的介紹和使用

vector學習時一定要學會查看reference 目錄 前言 一、vector基本概念 1.1vector是什么&#xff1f; 1.2內存管理 二、vector的使用 2.1vector的構造 2.2vector iterator 的使用 2.3vector 空間增長問題 2.4vector的元素訪問 2.5vector 增刪查改 總結 前言 在C編程中&#x…

iOS OC 圖片壓縮

純代碼,不廢話,歡迎copy使用,記得點贊 +(NSData *)imageData:(UIImage *)image maxSize:(int)maxSize{ // 設置最大文件大小(200KB) NSLog(@"執行壓縮方案 期望壓縮目標%dk",maxSize); return [self compressImage:image toMaxSize:maxSize]; } // 主壓縮方…

如何更改 SQLserver 數據庫存儲的位置 想從C盤換到D盤

在 SQL Server 中更改數據庫存儲位置&#xff08;從 C 盤遷移到 D 盤&#xff09;需要通過以下步驟完成&#xff1a;1. 確定數據庫文件的當前位置首先查詢數據庫文件的當前路徑&#xff1a;sqlSELECT name, physical_name AS current_location FROM sys.master_files WHERE dat…

【unitrix】 6.8 加一運算(add_one.rs)

一、源碼 這是一個使用 Rust 類型系統實現二進制數加一操作的代碼。 use crate::number::{O, I, B, Null, Bit, NormalizeIf};/// 類型級加一操作 trait /// /// 為二進制數類型實現加一操作&#xff0c;返回新的類型 pub trait AddOne {/// 加一操作的結果類型type Output;//…

國內Ubuntu訪問不了github、 huggingface等

各位小伙伴們&#xff0c;大家好呀。 大家是不是經常遇到訪問不了github、huggingface的情況呀。 在Ubuntu中可以這樣做。 訪問這個網站網站測速-Ping檢測-Trace查詢-Dig查詢-路由跟蹤查詢-tools.ipip.net&#xff0c; 對于github.com&#xff0c;在這個網站輸入github.com…

「Java EE開發指南」如何用MyEclipse創建企業應用項目?(一)

由于有了項目模型和管理工具&#xff0c;現在可以創建Java EE企業應用程序。在本文中您將了解到&#xff1a; 企業應用項目模型項目組織、依賴關系和類解析 該特性在MyEclipse中可用。 MyEclipse v2025.1離線版下載 1. 企業應用項目模型 MyEclipse提供了一個企業應用程序項…

ubuntu 22.04 pam 模塊設置用戶登錄失敗鎖定

1、ubuntu 22.04 配置方法 /etc/pam.d/common-auth 加到如下行后 # auth [success1 defaultignore] pam_unix.so nullok # 添加如下內容 auth [defaultdie] pam_faillock.so authfail auth sufficient pam_faillock.so authsucc/etc/pam.d/common…

Linux 定時任務全解析:atd 與 crond 的區別及實戰案例(含日志備份 + 時間寫入)

1. atd 和 crond 兩個任務管理程序的區別atd&#xff1a;用于執行一次性的定時任務&#xff0c;即設置任務在某個特定的時間點僅執行一次 &#xff0c;適合處理不需要重復執行的定時操作&#xff0c;比如在未來某個確切時間執行一個腳本、發送一份文件等場景。crond&#xff1a…

iOS加固工具有哪些?項目場景下的組合策略與實戰指南

在如今的iOS項目中&#xff0c;“加固”不僅是單一手段&#xff0c;更是多工具協同應用的過程。不同項目場景對安全要求的側重點不同&#xff0c;需要針對性地組合加固工具&#xff0c;才能最大化兼顧安全性、兼容性與效率。 本文將從常見項目場景出發&#xff0c;分析當下市面…

Xilinx Zynq:一款適用于軟件定義無線電的現代片上系統

摘要——軟件定義無線電可以在通用處理器 (CPU) 上實現&#xff0c;例如基于 PC 的處理器。處理器具有高度靈活性&#xff1a;它不僅可以用來處理數據樣本&#xff0c;還可以控制接收器功能、顯示瀑布圖或運行解調軟件。然而&#xff0c;由于處理速度相對較慢&#xff0c;處理器…

接口黑洞?破!安全堡壘?筑!冰火煉獄?戰!MES7114W終極掌控

在工業4.0加速推進的時代&#xff0c;設備互聯正面臨三大關鍵挑戰&#xff1a;多協議接口的“通信割裂”、極端環境的嚴苛考驗&#xff0c;以及高危場景下的安全紅線。在礦山井下、冶金車間、化工廠區等惡劣環境中&#xff0c;傳統有線方案往往受限于高成本布線、維護困難和環境…

深入理解進程地址空間:虛擬內存與進程獨立性

目錄 引言 虛擬地址空間的本質 關鍵觀察 進程地址空間布局 虛擬內存管理&#xff1a;mm_struct 虛擬內存的優勢 總結 引言 在操作系統中&#xff0c;每個進程都運行在自己的獨立區域中&#xff0c;這個區域就是??進程地址空間??。今天我們就來探討這個看似真實實則虛…

Apache ActiveMQ 任意文件寫入漏洞(CVE-2016-3088)復現利用

漏洞原理 Apache ActiveMQ是Apache軟件基金會所研發的開放源代碼消息中間件&#xff0c;由于ActiveMQ是一個純Java程序&#xff0c;因此只需要操作系統支持Java虛擬機&#xff0c;ActiveMQ便可執行 本漏洞出現在fileserver應用中&#xff0c;漏洞原理其實非常簡單&#xff0c…

谷歌地球與ArcGIS Pro查看三維地形

&#xff08;1&#xff09;Google Earth Web端 通過網站&#xff1a;https://earth.google.com/&#xff0c;進入谷歌地球Web端&#xff0c;可以查看歷史影像、三維地形數據、導入kml文件等。 &#xff08;2&#xff09;ArcGIS Pro查看三維場景 加載3D地形數據&#xff0c;轉…

Day06_C語言網絡編程20250718

01.思維導圖1 什么是 modbus他是一個在工控領域非常好用的通信寫 modbus協議本質上是一個 基于 tcp 協議二次封裝的一個協議 什么叫做基于tcp二次封裝的協議&#xff1a;我們自己寫的pack_t(無論靜態還是動態)&#xff0c;都是屬于二次封裝的協議modbus協議是一種 “主從問答式…

web開發-HTML

web開發——HTML 學習目標&#xff1a;學習HTML的基礎&#xff0c;學會get和post方法區別 一、HTMLHTML是什么&#xff1f; 前端網頁界面開發語言。開發工具 PyCharm、vscodePyCharm個性化設置&#xff08;字體和背景顏色&#xff09; File - setting - appearance - theme&…

主流編程語言全景圖:從Python到Rust的深度解析

2024年編程語言生態報告顯示&#xff0c;全球開發者使用的語言數量已達260&#xff0c;但真正主導行業的不到20種。本文帶你穿透技術迷霧&#xff0c;掌握8大核心語言的本質差異。一、選擇編程語言的黃金標準圖表代碼二、八大主流語言對比解析1. Python - 通用膠水語言特性&…