跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ 红黑树原理与实现详解

综述由AI生成红黑树是自平衡二叉搜索树,通过颜色标记和旋转操作维持高度在 O(log N)。本文解析了红黑树的五大性质、节点结构及插入查找的核心逻辑,重点阐述了插入时如何通过变色和旋转修复平衡,并提供了验证函数实现。虽然删除操作较复杂未展开,但掌握插入机制已能理解其自平衡本质。

接口猎人发布于 2026/3/22更新于 2026/5/13 浏览
C++ 红黑树原理与实现详解

C++ 红黑树原理与实现详解

红黑树作为自平衡二叉搜索树的代表,广泛应用于需要高效查找、插入和删除的场景,比如 STL 中的 map 和 set。虽然基本原理并不复杂,但实现细节繁多,特别是颜色调整和旋转操作。本文将结合实际代码,剖析红黑树的结构与核心操作。

红黑树的基本规则

红黑树是一种满足特定性质的二叉搜索树。每个节点附加了颜色属性(红色或黑色),通过以下五条规则保证树的平衡性:

  1. 每个节点的颜色要么是红色,要么是黑色。
  2. 根节点是黑色的。
  3. 如果一个节点是红色的,那么它的两个子节点必须是黑色的。 这意味着不能有两个连续的红色节点。
  4. 从任意一个节点到其所有叶子节点的路径上,必须包含相同数量的黑色节点。 这条规则保证了树的平衡性,防止路径过长。
  5. 所有叶子节点(NIL)都是黑色的。

这些规则通过'颜色约束'间接实现自平衡,因此每次进行插入、删除操作时,都需要确保这些规则被满足。

红黑树示例 红黑树示例 红黑树示例 红黑树示例 红黑树示例

高度与效率分析

红黑树的高度是其关键特性。自平衡机制确保了高度不会太大,这对操作效率至关重要。理论上,红黑树的高度 h 与黑色高度 bh(从根到叶子节点的路径上黑色节点的数量)之间存在如下关系:

最短路径(只有黑色节点)长度为 bh。最长路径(交替的红色和黑色节点)长度为 2 * bh。

因此,红黑树的最大高度为 2 * bh,最小高度为 bh。由于红黑树的黑色高度是相同的,可以得出红黑树的高度 h 满足 h ≤ 2 × bh,并且由于 N 个节点的红黑树其黑色高度至少为 log(N+1),所以红黑树的高度是 O(log N)。这使得查找、插入、删除操作在最坏情况下的时间复杂度均为 O(log N)。

红黑树高度示意图

节点结构定义

红黑树的基本节点结构包括键值对、左右子树指针、父节点指针以及颜色属性。

enum Colour { RED, BLACK };

template<class K, class V>
struct RBTreeNode {
    pair<K, V> _kv;           // 存储键值对
    RBTreeNode<K, V>* _left;  // 左子树
    RBTreeNode<K, V>* _right; // 右子树
    RBTreeNode<K, V>* _parent;// 父节点
    Colour _col;              // 颜色属性

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

每个节点不仅包含数据,还通过 _parent 指针方便向上回溯调整颜色,这是实现旋转和修复的关键。

插入操作实现

插入操作分为两步:首先按二叉搜索树规则找到位置插入新节点,然后将新节点设为红色并修复可能破坏的规则。

为什么新节点默认设为红色?因为如果设为黑色,会直接增加某条路径的黑色节点数,违反规则 4,修复成本更高;而设为红色只可能违反规则 3(连续红色),相对容易处理。

修复逻辑主要分两种情况:

  1. 叔叔节点存在且为红色:变色操作。父节点和叔叔变黑,祖父变红,然后继续向上处理。
  2. 叔叔节点不存在或为黑色:旋转操作。根据当前节点、父节点、祖父节点的相对位置,进行单旋或双旋,并调整颜色。

下面直接看核心插入逻辑的实现:

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;

    // 循环修复颜色冲突
    while (parent && parent->_col == RED) {
        Node* grandfather = parent->_parent;
        if (parent == grandfather->_left) {
            Node* uncle = grandfather->_right;
            // 情况 1:叔叔节点存在且为红色
            if (uncle && uncle->_col == RED) {
                parent->_col = uncle->_col = BLACK;
                grandfather->_col = RED;
                cur = grandfather;
                parent = cur->_parent;
            } else {
                // 情况 2:叔叔节点不存在或为黑色
                if (cur == parent->_left) {
                    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) {
                parent->_col = uncle->_col = BLACK;
                grandfather->_col = RED;
                cur = grandfather;
                parent = cur->_parent;
            } else {
                if (cur == parent->_right) {
                    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;
}

这段代码展示了插入时的核心修复逻辑。关键点在于通过循环不断向上检查,一旦遇到红色父节点就尝试修复,直到根节点或不再违反规则为止。

查找操作

查找操作和普通二叉搜索树类似,利用红黑树的平衡性,时间复杂度稳定在 O(log N)。

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

删除操作说明

删除操作比插入更复杂。当删除的是黑色节点时,可能会破坏黑色高度平衡,需要执行复杂的旋转和变色来恢复。尽管删除操作逻辑繁琐,但其时间复杂度同样是 O(log N)。这里不再展开具体实现,建议查阅《算法导论》或《STL 源码剖析》获取完整推导。

验证函数

为了验证红黑树的正确性,我们需要递归检查是否满足四条基本规则。可以通过前序遍历每一条路径,检查节点颜色和黑色节点数量是否符合要求。

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

通过这个检查函数,我们能够验证树的结构是否符合红黑树的规则。

总结

红黑树通过简单的颜色规则和旋转操作保证了树的平衡性,确保了查找、插入和删除操作的时间复杂度始终为 O(log N)。虽然插入和删除的实现细节较多,但它的高效性和稳定性使其成为许多应用中不可或缺的数据结构。掌握红黑树的插入逻辑,对于理解底层容器如 std::map 的工作原理非常有帮助。

目录

  1. C++ 红黑树原理与实现详解
  2. 红黑树的基本规则
  3. 高度与效率分析
  4. 节点结构定义
  5. 插入操作实现
  6. 查找操作
  7. 删除操作说明
  8. 验证函数
  9. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 前端 API 设计最佳实践:构建优雅的接口规范
  • 基于 SpringBoot 和 Vue 的高考志愿填报系统设计
  • whisper-large-v3-turbo 模型一键部署指南
  • OpenClaw + cpolar 实现本地 AI 公网访问实战指南
  • 小米手机端 Agent 落地,撬开智能家居十年困局
  • C++ 模板进阶:非类型参数、特化与分离编译
  • OpenClaw 树莓派部署:Gateway 仪表盘登录与网络配置排查
  • PyCharm 安装 Python 模块失败?常见 pip 报错原因与解决方案
  • AI 图像生成指南:从原理到实战
  • Mac Mini M4 本地 AI 实战:Ollama 与 SD 配置指南
  • Linux 与 Windows 系统 traceroute 与 tracert 命令详解
  • 大模型百科:核心概念、架构解析与学习路径
  • Python 异步编程实战:基于 async/await 的高并发实现
  • Python 开源 AI 模型引入与测试全流程实战
  • Mujoco 足式机器人强化学习:URDF 转 XML 配置指南
  • Python 爬虫开发与项目实战指南:从入门到分布式架构
  • C++ 20 协程入门指南
  • Android 应用冷启动流程深度解析
  • VSCode 本地运行 DeepSeek 模型教程
  • Python 兼职平台推荐与接私活实战指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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