紅黑樹的特性與實現

在數據結構領域,二叉搜索樹(BST)憑借 O (log n) 的平均時間復雜度成為查找、插入和刪除操作的優選結構。但它有個致命缺陷:當輸入數據有序時,會退化為鏈表,時間復雜度驟降至 O (n)。為解決這一問題,計算機科學家設計了多種自平衡二叉搜索樹,紅黑樹(Red-Black Tree)便是其中應用最廣泛的一種。它通過巧妙的著色規則和有限的旋轉操作,在保證平衡的同時將維護成本降到最低,成為 STL 容器、Linux 內核等工業級系統的核心數據結構。本文將從底層原理到工程實踐,全面剖析紅黑樹的工作機制。

一、紅黑樹的核心特性與平衡原理

紅黑樹是一種自平衡二叉搜索樹,其每個節點除了存儲鍵值、左右子節點和父節點指針外,還額外包含一個顏色屬性(紅色或黑色)。通過嚴格遵循以下 5 條性質,紅黑樹實現了結構平衡:

  1. 顏色約束:每個節點要么是紅色,要么是黑色。
  2. 根節點規則:根節點必須是黑色。
  3. 葉子節點規則:所有葉子節點(NIL 節點,即空節點)都是黑色。
  4. 連續紅色禁止:如果一個節點是紅色,則它的兩個子節點必須是黑色(不存在連續的紅色節點)。
  5. 黑色平衡規則:從任意節點到其所有后代葉子節點的路徑中,包含的黑色節點數量相同(稱為 "黑高")。

平衡原理深度解析

這些特性共同構建了紅黑樹的 "平衡能力":

  • 性質 4 避免了 "紅色鏈" 的無限延伸,限制了樹的傾斜程度;
  • 性質 5 保證了所有路徑的 "黑高" 一致,而紅色節點僅能穿插在黑色節點之間;
  • 由此可推導出關鍵結論:紅黑樹中最長路徑的長度不會超過最短路徑的 2 倍(最短路徑全為黑色節點,最長路徑為黑紅交替)。

這一結論直接確保了紅黑樹的高度始終為 O (log n),從而保證了所有操作的最壞時間復雜度為 O (log n)。

二、紅黑樹的節點結構與基礎定義

2.1 節點結構設計

紅黑樹的節點需要存儲 5 類信息:鍵值、顏色、左子節點、右子節點和父節點。為簡化邊界條件處理(如空節點的顏色判斷),通常將所有空節點統一指向一個哨兵節點(NIL 節點),該節點永久為黑色,且左右子節點和父節點均指向自身。

enum Color { RED, BLACK };// 節點結構定義
struct Node {int key;          // 鍵值Color color;      // 節點顏色Node *left;       // 左子節點Node *right;      // 右子節點Node *parent;     // 父節點// 構造函數:新節點默認紅色(插入時優化)Node(int k) : key(k), color(RED), left(nullptr), right(nullptr), parent(nullptr) {}
};// 哨兵節點定義(全局唯一)
Node *NIL_NODE = new Node(0);
NIL_NODE->color = BLACK;
NIL_NODE->left = NIL_NODE;
NIL_NODE->right = NIL_NODE;
NIL_NODE->parent = NIL_NODE;

2.2 關鍵設計細節

  • 哨兵節點的作用:將所有空指針替換為 NIL_NODE,避免在操作中頻繁判斷nullptr,統一邊界處理邏輯;
  • 新節點默認紅色:插入紅色節點僅可能違反性質 4(連續紅色),而插入黑色節點會直接違反性質 5(黑高不一致),前者修復成本更低;
  • 父節點指針的必要性:紅黑樹的旋轉和修復操作需要回溯到祖先節點,必須通過父指針實現向上遍歷。

三、紅黑樹的核心操作:插入與修復

3.1 插入流程

紅黑樹的插入操作分為兩步:

  1. 按 BST 規則插入:找到合適位置插入新節點(默認紅色);
  2. 修復紅黑樹性質:通過旋轉和變色消除對紅黑樹性質的破壞。
插入核心代碼(BST 部分
void insert(Node *&root, int key) {Node *z = new Node(key);Node *y = NIL_NODE;  // 記錄x的父節點Node *x = root;// 查找插入位置while (x != NIL_NODE) {y = x;if (z->key < x->key) {x = x->left;} else {x = x->right;}}// 確定新節點的父節點z->parent = y;if (y == NIL_NODE) {root = z;  // 樹為空,新節點成為根} else if (z->key < y->key) {y->left = z;} else {y->right = z;}// 初始化新節點的子節點為哨兵z->left = NIL_NODE;z->right = NIL_NODE;z->color = RED;  // 新節點默認紅色// 修復紅黑樹性質insertFixup(root, z);
}

3.2 插入后的修復操作(insertFixup)

插入紅色節點后,可能違反的性質為:

  • 性質 2(根節點為紅色):僅當插入的是第一個節點時可能發生;
  • 性質 4(連續紅色節點):當父節點為紅色時發生。

修復過程通過循環處理,直到上述性質均被滿足。根據 "叔節點"(父節點的兄弟節點)的顏色,分為 3 種情況:

情況 1:叔節點為紅色
  • 場景:新節點(z)的父節點(p)為紅色,叔節點(u)為紅色;
  • 破壞性質:僅可能違反性質 4(連續紅色);
  • 修復邏輯
    1. 將父節點(p)和叔節點(u)改為黑色;
    2. 將祖父節點(g)改為紅色;
    3. 令 z = g,繼續向上修復(祖父節點可能與它的父節點形成連續紅色)。
// 情況1:叔節點為紅色
case 1:z->parent->parent->left->color = BLACK;  // 叔節點變黑z->parent->parent->right->color = BLACK; // 父節點變黑z->parent->parent->color = RED;          // 祖父節點變紅z = z->parent->parent;                   // 向上追溯break;
情況 2:叔節點為黑色,且新節點是右孩子
  • 場景:父節點(p)為紅色,叔節點(u)為黑色,z 是右孩子;
  • 破壞性質:性質 4(連續紅色);
  • 修復邏輯
    1. 對父節點(p)執行左旋,將 z 轉為左孩子;
    2. 轉化為情況 3 處理(統一修復邏輯)。
// 情況2:叔節點為黑色,z是右孩子
case 2:z = z->parent;              // z指向父節點leftRotate(root, z);        // 左旋父節點// 轉化為情況3
情況 3:叔節點為黑色,且新節點是左孩子
  • 場景:父節點(p)為紅色,叔節點(u)為黑色,z 是左孩子;
  • 破壞性質:性質 4(連續紅色);
  • 修復邏輯
    1. 對祖父節點(g)執行右旋;
    2. 交換父節點(p)和祖父節點(g)的顏色;
    3. 修復完成(無需繼續向上追溯)。
// 情況3:叔節點為黑色,z是左孩子
case 3:z->parent->color = BLACK;   // 父節點變黑z->parent->parent->color = RED;  // 祖父節點變紅rightRotate(root, z->parent->parent);  // 右旋祖父節點z = root;  // 退出循環break;

3.3 旋轉操作的實現

旋轉是紅黑樹維護平衡的核心手段,分為左旋和右旋,作用是改變節點間的父子關系而不破壞 BST 性質。

左旋操作(leftRotate)
void leftRotate(Node *&root, Node *x) {Node *y = x->right;  // y是x的右子節點x->right = y->left;  // 將y的左子樹轉為x的右子樹if (y->left != NIL_NODE) {y->left->parent = x;  // 更新y左子樹的父節點}y->parent = x->parent;  // 更新y的父節點// 處理x是根節點的情況if (x->parent == NIL_NODE) {root = y;} else if (x == x->parent->left) {x->parent->left = y;  // x是左孩子時,y替代x的位置} else {x->parent->right = y; // x是右孩子時,y替代x的位置}y->left = x;  // x成為y的左孩子x->parent = y;  // 更新x的父節點
}
右旋操作(rightRotate)

右旋與左旋對稱,核心是將左子節點提升為父節點,此處不再贅述。

四、紅黑樹的刪除操作與修復

刪除操作是紅黑樹中最復雜的部分,核心挑戰是如何在刪除節點后維持紅黑樹的 5 條性質。刪除流程分為 3 步:

  1. 按 BST 規則刪除節點:找到待刪除節點,根據節點的子節點數量(0、1、2)執行不同刪除邏輯;
  2. 記錄關鍵信息:跟蹤 "替代節點"(實際被移除的節點)及其原始顏色;
  3. 修復紅黑樹性質:若刪除的是黑色節點,需通過修復流程恢復平衡。

4.1 BST 刪除邏輯

  • 葉子節點(無孩子):直接刪除;
  • 單孩子節點:用子節點替代該節點;
  • 雙孩子節點:找到中序后繼(右子樹最小節點),復制其值到當前節點,再刪除后繼節點(轉化為前兩種情況)。

4.2 刪除后的修復操作(deleteFixup)

刪除節點后,若被刪除節點是黑色,會破壞性質 5(黑高不一致),此時需要通過 "雙重黑色" 標記來修復。"雙重黑色" 表示該路徑上缺少一個黑色節點,需要通過以下 4 種情況消除:

情況 1:兄弟節點為紅色
  • 場景:當前節點(x)為雙重黑色,兄弟節點(s)為紅色;
  • 修復邏輯
    1. 將兄弟節點(s)改為黑色,父節點(p)改為紅色;
    2. 對父節點(p)執行左旋;
    3. 轉化為其他情況(兄弟節點變為黑色)。
情況 2:兄弟節點為黑色,且兄弟的兩個孩子均為黑色
  • 場景:x 為雙重黑色,s 為黑色,s 的左右孩子均為黑色;
  • 修復邏輯
    1. 將兄弟節點(s)改為紅色;
    2. 令 x = p(將雙重黑色上移);
    3. 繼續向上修復。
情況 3:兄弟節點為黑色,左孩子紅色,右孩子黑色
  • 場景:x 為雙重黑色,s 為黑色,s 的左孩子紅、右孩子黑;
  • 修復邏輯
    1. 將 s 的左孩子改為黑色,s 改為紅色;
    2. 對 s 執行右旋;
    3. 轉化為情況 4。
情況 4:兄弟節點為黑色,右孩子為紅色
  • 場景:x 為雙重黑色,s 為黑色,s 的右孩子為紅色;
  • 修復邏輯
    1. 將 s 的顏色改為 p 的顏色;
    2. 將 p 改為黑色,s 的右孩子改為黑色;
    3. 對 p 執行左旋;
    4. 令 x = root(修復完成)。

4.3 修復操作的核心目標

  • 消除 "雙重黑色" 標記,恢復路徑上的黑高平衡;
  • 避免出現連續紅色節點,維持性質 4;
  • 通過最少的旋轉和變色操作完成修復,降低時間開銷。

五、紅黑樹與 AVL 樹的深度對比

特性紅黑樹AVL 樹
平衡標準顏色規則 + 黑高平衡左右子樹高度差≤1(嚴格平衡)
旋轉次數插入最多 2 次,刪除最多 3 次插入最多 2 次,刪除可能 O (log n)
空間開銷存儲顏色(1bit)存儲平衡因子(通常需 2-4bit)
查找性能平均 O (log n),最壞 O (2log n)嚴格 O (log n)(高度更優)
插入 / 刪除性能更優(旋轉少)較差(嚴格平衡導致旋轉頻繁)
適用場景插入刪除頻繁的場景(如 STL 容器)查找頻繁的場景(如數據庫索引)

紅黑樹的工業級優勢:在大多數實際場景中,紅黑樹的綜合性能優于 AVL 樹。雖然 AVL 樹的高度更優,但紅黑樹的旋轉操作更少,維護成本更低,尤其適合插入刪除頻繁的動態數據場景。

六、紅黑樹的實際應用場景

  1. C++ STL 容器std::mapstd::setstd::multimap等關聯容器底層均采用紅黑樹實現,利用其 O (log n) 的插入、刪除和查找性能;
  2. Linux 內核
    • 進程調度:CFS(完全公平調度器)用紅黑樹管理進程運行隊列,快速找到下一個待調度進程;
    • 虛擬內存管理:VM_area_struct 結構體用紅黑樹組織,高效管理內存區域;
  3. Java 集合框架TreeMapTreeSet底層基于紅黑樹,提供有序映射和集合功能;
  4. 數據庫:部分數據庫(如 MongoDB)的索引結構采用紅黑樹,平衡查詢和更新性能;
  5. 編譯器實現:語法分析階段的符號表常用紅黑樹存儲變量和函數信息,支持快速查找和插入。

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

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

相關文章

ClickHouse從入門到企業級實戰全解析課程簡介

【課程簡介】你是否正在面臨這些挑戰&#xff1f;海量數據的分析查詢慢如蝸牛&#xff0c;報表一等就是幾小時&#xff1f;想構建實時數倉&#xff0c;卻不知如何高效處理 Kafka 等流式數據&#xff1f;對 ClickHouse 的眾多 MergeTree 引擎感到困惑&#xff0c;不知如何選型&a…

【新啟航】從人工偏差到機械精度:旋轉治具讓三維掃描重構數據重復精度提升至 ±0.01mm

在三維掃描重構領域&#xff0c;傳統人工操作方式受限于人為因素干擾&#xff0c;數據重復精度難以保證&#xff0c;無法滿足高精度工業檢測與逆向工程需求。旋轉治具憑借先進的機械設計與自動化控制技術&#xff0c;將三維掃描重構數據重復精度提升至 0.01mm&#xff0c;實現從…

《匯編語言:基于X86處理器》第13章 復習題和編程練習

本篇記錄了《匯編語言&#xff1a;基于X86處理器》第13章 復習題和編程練習的學習筆記。13.6 復習題1.當匯編過程被高級語言程序調用時&#xff0c;主調程序與被調過程是否應使用相同的內存模式?答&#xff1a;主調程序與被調過程使用的內存模式必須相同。2.C 和 C程序調用匯編…

SpringAI智能航空助手實戰<Demo>

我們將如何將我們得傳統業務進行智能化的改造>>>1.將我們傳統的航空票務系統 我們之前通過按鈕的方式來完成 現在我們通過智能對話的方式完成 >現在我們通過對話的方式來完成 整個智能化的改造 傳統應用如何進行智能化改造 我們把我們的項目通過Spring-ai 來接入A…

windows git安裝步驟

1&#xff0c;從官網下載安裝包&#xff1a;gitg官網 進行安裝 2&#xff0c;配置git環境&#xff1a; git config --global user.name "Your Name" git config --global user.email "Your Email"3&#xff0c;生成 SSH Key&#xff1a; ssh-keygen -t r…

使用chroma和LlamaIndex做RAG增強

RAG 原理&#xff1a;通過 “檢索&#xff08;從知識庫獲取相關信息&#xff09;→ 增強&#xff08;將信息作為上下文輸入模型&#xff09;→ 生成&#xff08;模型基于上下文回答&#xff09;” 三步&#xff0c;解決大模型知識時效性、領域局限性問題。 接下來將完成這么一個…

2025 最應避免的攝影陷阱以及解決方案

你有沒有想過&#xff0c;當你拍完了一個完美的場景后&#xff0c;卻發現畫面模糊、光線不足&#xff0c;或者更糟的是&#xff0c;存儲卡中的文件丟失了&#xff1f;這些問題可能會發生在任何人身上&#xff0c;無論是業余愛好者、專業人士還是最好的攝影師。當珍貴的記憶變成…

python類--python011

面向對象編程中的類的概念、屬性使用、繼承和類的改造問題等。7.1 初識類在軟件編程中&#xff0c;面向過程和面向對象是兩種主要的編程方法。面向過程的編程強調通過函數來實現特定的功能&#xff0c;具有靈活性&#xff0c;但在復雜系統中往往導致代碼重復&#xff0c;維護困…

Python函數篇:從零到精通

一、函數1.1 為什么有函數我們對于一個項目時&#xff0c;會有上千甚至上萬條代碼&#xff0c;當我們要使用到某個函數時&#xff0c;例如我需要計算一個求和代碼&#xff0c;獲得求和的值來服務我們的項目&#xff0c;那我們可能會這樣#計算1&#xff5e;100的和 theSun 0 fo…

QT項目之記事本

本文用QT實現記事本功能。一、成品展示1.界面主要元素&#xff1a;1.標題為MyNoteBook&#xff1b;2.相應圖標為&#xff1a;打開文件&#xff0c;保存&#xff0c;退出&#xff1b;3.右下角標注光標所在行列&#xff0c;默認編碼方式為UTF-8&#xff1b;4.鼠標所在圖標位置時會…

【軟件測試】性能測試 —— 工具篇 JMeter 介紹與使用

&#x1f970;&#x1f970;&#x1f970;來都來了&#xff0c;不妨點個關注叭&#xff01; &#x1f449;博客主頁&#xff1a;歡迎各位大佬!&#x1f448; 文章目錄1. JMeter 的介紹2. JMeter 安裝、配置、搭建2.1 前置條件 —— Java環境搭建2.2 JMeter 下載2.3 JMeter 安裝…

二十二、Mybatis-快速入門程序

入門程序大概步驟敘述&#xff1a; 步驟一&#xff1a;創建springboot工程并且數據庫提前創建表步驟二&#xff1a;創建springboot工程對Mybatis相關依賴注意打勾步驟三&#xff1a;編寫查找方法步驟四&#xff1a;編寫測試方法項目目錄結構與數據庫以及代碼&#xff1a; 項目目…

Blender模擬結構光3D Scanner(一)外參數匹配

如何使用Blender模擬FPP(Fringe Projection Profilometry) 原理的結構光3D傳感器&#xff1f;主要包含的工作有&#xff1a;1&#xff09;相機、投影儀定位與內外參數匹配&#xff1b;2&#xff09;投影儀投射指定Pattern圖像&#xff1b;3&#xff09;被測物體材質屬性配置等&…

LangChain是如何實現RAG多輪問答的

目錄引言一、LangChain實現RAG多輪問答核心機制1. 對話歷史管理&#xff08;Memory&#xff09;2. 問題重寫&#xff08;Query Rewriting&#xff09;3. 檢索增強生成&#xff08;RAG Core&#xff09;4. 鏈式工作流&#xff08;Chain&#xff09;二、關鍵設計特點三、完整示例…

DAY 44 預訓練模型

知識點回顧&#xff1a; 預訓練的概念常見的分類預訓練模型圖像預訓練模型的發展史預訓練的策略預訓練代碼實戰&#xff1a;resnet18 一、預訓練的概念 我們之前在訓練中發現&#xff0c;準確率最開始隨著epoch的增加而增加。隨著循環的更新&#xff0c;參數在不斷發生更新。 所…

Java Stream API 中常用方法復習及項目實戰示例

在最近的練手項目中&#xff0c;對于stream流的操作愈加頻繁&#xff0c;我也越來越感覺stream流在處理數據是的干凈利落&#xff0c;因此寫博客用來記錄最近常用的方法以便于未來的復習。map() 方法map()是一個中間操作&#xff08;intermediate operation&#xff09;&#x…

從零開始手搓一個GPT大語言模型:從理論到實踐的完整指南(一)

現在人工智能飛速發展時代&#xff0c;LLM絕對可以算是人工智能領域得一顆明珠&#xff0c;也是現在許多AI項目落地得必不可少得一個模塊&#xff0c;可以說&#xff0c;不管你之前得研究領域是AI得哪個方向&#xff0c;現在都需要會一些LLM基礎&#xff0c;在這個系列&#xf…

Redis ubuntu下載Redis的C++客戶端

1. 安裝 redis-plus-plus C 操作 Redis 的庫有很多&#xff0c;這里選擇使用 redis-plus-plus&#xff0c;這個庫的功能強大&#xff0c;使用簡單。 Github 地址&#xff1a;GitHub - sewenew/redis-plus-plus: Redis client written in C 訪問不了Github 地址的可以使用Ste…

nm命令和nm -D命令參數

出現這種差異的原因在于&#xff1a;動態庫中的符號分為兩種類型&#xff1a; 常規符號表&#xff08;regular symbol table&#xff09;&#xff1a;通常用于靜態鏈接和調試&#xff0c;默認不包含在動態庫中&#xff08;除非顯式保留&#xff09;。動態符號表&#xff08;dyn…

Windows下cuda的安裝和配置

今天開始做一個cuda教程。由于本人主要在windows下使用visual studio進行開發&#xff0c;因此這里講一下windows下的cuda開發環境。 下載cuda_toolkit 從網站https://developer.nvidia.com/cuda-toolkit中下載&#xff0c;先選擇Download Now,然后跳轉到如下頁面&#xff1a…