跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 红黑树核心原理与完整实现

红黑树作为 C++ STL 底层容器的重要数据结构,通过颜色约束实现近似平衡。详细阐述了红黑树的五条核心性质,分析了最长路径不超过最短路径两倍的数学原理。重点讲解了插入操作的三种调整情况:叔叔节点为红色的变色处理,以及叔叔节点为黑色时的单旋与双旋配合变色策略。提供了完整的 C++ 节点结构定义、插入逻辑及验证函数代码,修正了常见笔误与指针管理细节,帮助开发者深入理解平衡树的维护机制。

BackendPro发布于 2026/3/15更新于 2026/10/2489 浏览
C++ 红黑树核心原理与完整实现

C++ 红黑树核心原理与完整实现

前言

在掌握了 AVL 树的平衡机制后,我们迎来了更实用且高效的平衡二叉搜索树——红黑树。它是 C++ STL 中 map 和 set 的底层基石。相比 AVL 树严格的平衡要求,红黑树通过颜色约束实现了近似平衡,在插入和删除操作上往往具有更好的性能表现。

1. 红黑树的概念与规则

1.1 什么是红黑树

红黑树是一种自平衡的二叉搜索树。每个节点增加一个存储位表示颜色(红色或黑色)。通过对路径上颜色的约束,确保没有一条路径会比其他路径长出两倍,从而保证整体接近平衡。

1.2 五大性质

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点必须是黑色。
  3. 如果一个节点是红色,则它的两个子节点必须是黑色(即不能有两个连续的红色节点)。
  4. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
  5. 所有的叶子节点(NULL)都是黑色的。

注意:这里的'叶子节点'通常指外部空节点,而非传统意义上的终端节点。

1.3 为什么最长路径不超过最短路径的两倍

根据性质 4,每条路径上的黑色节点数量(黑高)是固定的,记为 $bh$。

  • 最短路径:全由黑色节点组成,长度为 $bh$。
  • 最长路径:红黑交替出现,由于性质 3 限制无连续红节点,长度最多为 $2 \times bh$。 因此,最长路径不会超过最短路径的两倍,时间复杂度稳定在 $O(\log N)$。

文章配图

2. 红黑树的结构与查找

2.1 节点结构

红黑树是三叉链表结构,需要记录父指针以便向上回溯进行旋转和变色操作。

enum Colour { RED, BLACK };

template<class K, class V>
struct RBTreeNode {
    pair<K, V> _kv;
    RBTreeNode* _left;
    RBTreeNode* _right;
    RBTreeNode* _parent;
    Colour _col;

    RBTreeNode(const pair<K, V>& kv) 
        :_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED) {}
};

2.2 查找操作

查找逻辑与普通二叉搜索树一致,效率为 $O(\log N)$。

Node* Find( K& key) {
    Node* cur = _root;
     (cur) {
         (cur->_kv.first > key) cur = cur->_left;
          (cur->_kv.first < key) cur = cur->_right;
          cur;
    }
     ;
}
const
while
if
else
if
else
return
return
nullptr

3. 红黑树的插入与平衡调整

插入是红黑树最复杂的部分。新节点默认插入为红色,若违反规则则需调整。

3.1 插入流程

  1. 若树为空,新节点设为根并染黑。
  2. 若树非空,按 BST 规则插入,新节点染红。
  3. 检查是否违反规则(主要是父子同红),若不违反直接结束;若违反,根据叔叔节点颜色分情况处理。

3.2 三种调整情况

假设当前节点为 cur,父节点为 p,祖父节点为 g,叔叔节点为 u。

情况一:叔叔节点存在且为红色

此时 p 和 u 均为红色,g 必为黑色。

  • 策略:变色。p 和 u 变黑,g 变红。
  • 后续:将 g 视为新的 cur 继续向上检查。若 g 变为根,则强制染黑。
情况二:叔叔节点不存在或为黑色,且为左左/右右型
  • 策略:单旋 + 变色。
  • 左左型:以 g 为轴右旋,p 变黑,g 变红。
  • 右右型:以 g 为轴左旋,p 变黑,g 变红。
  • 结果:调整后无需继续向上,因为局部已满足性质。
情况三:叔叔节点不存在或为黑色,且为左右/右左型
  • 策略:双旋 + 变色。
  • 左右型:先以 p 左旋,再以 g 右旋。
  • 右左型:先以 p 右旋,再以 g 左旋。
  • 变色:旋转后 cur 变黑,g 变红。

3.3 代码实现

以下是完整的插入逻辑,包含了旋转和变色的统一处理框架。

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->_left;
        } else if (cur->_kv.first < kv.first) {
            parent = cur;
            cur = cur->_right;
        } else {
            return false; // 重复节点
        }
    }

    cur = new Node(kv);
    cur->_col = RED;
    cur->_parent = parent;

    if (parent->_kv.first < kv.first) parent->_right = cur;
    else parent->_left = cur;

    // 修复红黑树性质
    while (parent && parent->_col == RED) {
        Node* grandfather = parent->_parent;
        
        if (grandfather->_left == parent) {
            Node* uncle = grandfather->_right;
            
            // 情况一:叔叔存在且为红
            if (uncle && uncle->_col == RED) {
                grandfather->_col = RED;
                parent->_col = BLACK;
                uncle->_col = BLACK;
                cur = grandfather;
                parent = cur->_parent;
            } 
            // 情况二 & 三:叔叔黑或不存在
            else {
                if (parent->_left == cur) {
                    // 左左型:右单旋
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                } else {
                    // 左右型:左右双旋
                    RotateL(parent);
                    RotateR(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                break; // 修复完成
            }
        } else {
            Node* uncle = grandfather->_left;
            
            if (uncle && uncle->_col == RED) {
                grandfather->_col = RED;
                parent->_col = BLACK;
                uncle->_col = BLACK;
                cur = grandfather;
                parent = cur->_parent;
            } 
            else {
                if (parent->_right == cur) {
                    // 右右型:左单旋
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                } else {
                    // 右左型:右左双旋
                    RotateR(parent);
                    RotateL(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                break;
            }
        }
    }
    _root->_col = BLACK; // 确保根节点始终为黑
    return true;
}

4. 验证与测试

编写完红黑树后,验证至关重要。主要检查两点:无连续红节点、任意路径黑节点数相等。

bool Check(Node* root, int blackNum, const int refNum) {
    if (root == nullptr) {
        if (refNum != blackNum) {
            cout << "黑节点数量不一致" << endl;
            return false;
        }
        return true;
    }
    
    // 检查连续红节点
    if (root->_col == RED && root->_parent && root->_parent->_col == RED) {
        cout << root->_kv.first << " 存在连续红节点" << endl;
        return false;
    }
    
    if (root->_col == BLACK) blackNum++;
    
    return Check(root->_left, blackNum, refNum) && 
           Check(root->_right, blackNum, refNum);
}

5. 关于删除

红黑树的删除操作比插入更为复杂,涉及更多的旋转和变色组合。在实际工程中,建议直接使用标准库中的 std::map 或 std::set。若需深入理解,可参考《算法导论》相关章节。

目录

  1. C++ 红黑树核心原理与完整实现
  2. 前言
  3. 1. 红黑树的概念与规则
  4. 1.1 什么是红黑树
  5. 1.2 五大性质
  6. 1.3 为什么最长路径不超过最短路径的两倍
  7. 2. 红黑树的结构与查找
  8. 2.1 节点结构
  9. 2.2 查找操作
  10. 3. 红黑树的插入与平衡调整
  11. 3.1 插入流程
  12. 3.2 三种调整情况
  13. 情况一:叔叔节点存在且为红色
  14. 情况二:叔叔节点不存在或为黑色,且为左左/右右型
  15. 情况三:叔叔节点不存在或为黑色,且为左右/右左型
  16. 3.3 代码实现
  17. 4. 验证与测试
  18. 5. 关于删除

更多推荐文章

查看全部
  • C++ 核心面试题总结:语法、内存与面向对象
  • OkHttp 源码分析:缓存策略实现
  • Java 从入门到企业级开发指南
  • VMware Workstation 17 下 Ubuntu 24.04 虚拟机卡死问题排查与解决
  • GitHub Copilot 与 Claude Code 功能对比分析
  • 多模态模型开发实战:文本、图像与语音融合应用指南
  • 从 vw/vh 到 clamp(),前端响应式设计的痛点与进化
  • 转行 Python 两年:从运营到爬虫工程师的个人成长之路
  • 零成本部署阿里开源 CoPaw:打造你的 AI 个人助理
  • Vue3 人机对话界面实现:流式请求与 ECharts 图表集成
  • RocketMQ 核心原理与架构解析:NameServer、Broker 及生产消费流程
  • 使用 Miniconda 创建 Python 3.8 环境指南
  • AI WebUI Chatbot 实战开发:从架构设计到生产部署
  • Android 手机本地运行 Stable Diffusion 的开源方案
  • CS336 从零构建语言模型:Transformer 架构实现详解
  • 图寻路算法详解:基于深度优先搜索 (DFS) 实现
  • Git 标签
  • AI 产品经理转型指南:核心能力与学习路径
  • 基于开源技术栈搭建地理信息 SaaS 化开发生态方案
  • 扩散模型(Diffusion Model)原理与图像生成实战

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online