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

红黑树详解:原理、规则与 C++ 实现

红黑树作为自平衡二叉搜索树,通过颜色约束确保最长路径不超过最短路径的两倍。其核心规则包括根节点为黑、红节点子节点必黑及路径黑高一致。插入操作需处理变色与旋转以维持平衡,时间复杂度稳定在 O(logN)。梳理了红黑树的定义、性质及 C++ 模板实现细节,涵盖单旋双旋场景下的调整逻辑。

锁机制发布于 2026/3/16更新于 2026/9/560 浏览
红黑树详解:原理、规则与 C++ 实现

红黑树详解

基本概念

红黑树是一种自平衡的二叉搜索树。它在每个节点上增加了一个存储位来表示颜色(红色或黑色),通过对路径上颜色的约束,确保从根到叶子的最长路径不会超过最短路径的两倍,从而近似保持平衡。

核心规则

要成为一棵合法的红黑树,必须满足以下五条性质:

  1. 节点颜色:每个节点要么是红色,要么是黑色。
  2. 根节点:根节点必须是黑色。
  3. 红色限制:如果一个节点是红色,则它的两个子节点必须是黑色。这意味着任意一条路径上不能出现连续的红色节点。
  4. 黑色高度:对于任意一个节点,从该节点到其所有叶子节点(NIL 节点)的简单路径上,均包含相同数量的黑色节点。
  5. 叶子节点:所有的叶子节点(NIL)都是黑色的。

注意:数路径时要数到空结点(NIL)。这保证了树的深度控制在 O(logN) 级别。

插入调整策略

插入新节点时,我们默认将其染为红色,然后按照二叉搜索树的规则找到位置。如果破坏了红黑树的性质,就需要通过变色和旋转来修复。主要涉及三种情况:

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

当父节点和当前节点都是红色,而叔叔节点也是红色时,说明祖父节点必然是黑色。此时只需将父节点和叔叔节点变黑,祖父节点变红,然后将祖父节点视为新的当前节点继续向上检查。这种情况只变色不旋转。

情况二:叔叔节点不存在或为黑色(单旋 + 变色)

当父节点和当前节点同侧(例如都是左孩子),且叔叔节点不存在或为黑色时,需要对祖父节点进行右旋,并交换颜色。这通常发生在新增节点导致不平衡时。

情况三:叔叔节点不存在或为黑色(双旋 + 变色)

当父节点和当前节点异侧(例如父是左,当前是右)时,需要先对父节点进行左旋,转化为情况二,然后再对祖父节点进行右旋。双旋操作能有效处理这种'之'字形的结构。

C++ 代码实现

下面是一个基于模板的红黑树实现示例。为了便于理解,我将核心逻辑整理成了清晰的类结构。在实际工程中,通常会配合 KeyOfT 函数对象来提取比较键值。

#include <iostream>
#include <vector>
using namespace std;

enum Colour { RED, BLACK };

template<class T>
struct RBTreeNode {
    T _data;
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    Colour _col;

    RBTreeNode(const T& data)
        :_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED) {}
};

template<class K, class T, class KeyOfT>
class RBTree {
    typedef RBTreeNode<T> Node;
public:
    bool Insert(const T& data) {
        if (_root == nullptr) {
            _root = new Node(data);
            _root->_col = BLACK;
            return true;
        }

        KeyOfT kot;
        Node* parent = nullptr;
        Node* cur = _root;

        // 1. 查找插入位置
        while (cur) {
            if (kot(cur->_data) < kot(data)) {
                parent = cur;
                cur = cur->_right;
            } else if (kot(cur->_data) > kot(data)) {
                parent = cur;
                cur = cur->_left;
            } else {
                return false; // 已存在
            }
        }

        // 2. 插入新节点(默认为红色)
        cur = new Node(data);
        cur->_col = RED;
        if (kot(parent->_data) < kot(data)) {
            parent->_right = cur;
        } else {
            parent->_left = cur;
        }
        cur->_parent = parent;

        // 3. 修复红黑树性质
        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/3: 叔叔不存在或为黑
                    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;
    }

    void InOrder() {
        _InOrder(_root);
        cout << endl;
    }

private:
    int _Size(Node* root) {
        return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
    }

    int _Height(Node* root) {
        if (root == nullptr) return 0;
        int leftHeight = _Height(root->_left);
        int rightHeight = _Height(root->_right);
        return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

    void _InOrder(Node* root) {
        if (root == nullptr) return;
        _InOrder(root->_left);
        cout << root->_data << " ";
        _InOrder(root->_right);
    }

    void RotateR(Node* parent) {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        parent->_left = subLR;
        if (subLR) subLR->_parent = parent;

        Node* ppnode = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;

        if (parent == _root) {
            _root = subL;
            subL->_parent = nullptr;
        } else {
            if (ppnode->_left == parent) ppnode->_left = subL;
            else ppnode->_right = subL;
            subL->_parent = ppnode;
        }
    }

    void RotateL(Node* parent) {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        parent->_right = subRL;
        if (subRL) subRL->_parent = parent;

        Node* ppnode = parent->_parent;
        subR->_left = parent;
        parent->_parent = subR;

        if (parent == _root) {
            _root = subR;
            subR->_parent = nullptr;
        } else {
            if (ppnode->_left == parent) ppnode->_left = subR;
            else ppnode->_right = subR;
            subR->_parent = ppnode;
        }
    }

private:
    Node* _root = nullptr;
};

总结

红黑树通过严格的颜色约束和旋转机制,在插入、删除和查找操作上都能保证 O(logN) 的时间复杂度。相比于 AVL 树,它减少了旋转次数,更适合频繁插入的场景。理解其插入时的三种调整情况,是掌握这一数据结构的关键。

目录

  1. 红黑树详解
  2. 基本概念
  3. 核心规则
  4. 插入调整策略
  5. 情况一:叔叔节点存在且为红色
  6. 情况二:叔叔节点不存在或为黑色(单旋 + 变色)
  7. 情况三:叔叔节点不存在或为黑色(双旋 + 变色)
  8. C++ 代码实现
  9. 总结

更多推荐文章

查看全部
  • 开源模型应用落地:安全合规 - 用户输入价值观判断(四)
  • 递归算法原理及经典例题讲解
  • C++ 异常处理机制详解
  • 2026 年推荐的 5 款主流 React UI 组件库
  • 知网 AIGC 检测原理与降重策略指南
  • 2025-0xGame Web 安全挑战全解
  • 使用 Cpolar 和 JuiceSSH 远程连接内网 Linux 虚拟机
  • 利用腾讯云 HAI 与 DeepSeek 快速构建个人网页
  • DBeaver 社区版 AI 助手配置指南
  • Intel 芯片 Mac 安装 Genymotion 安卓虚拟机指南
  • AI 图像生成指南:从原理到实战
  • Java 核心面试题与解析汇总
  • MySQL 8.0 安装配置与连接实战指南
  • Android 插件化核心:ClassLoader 机制与原理详解
  • VRCT 智能翻译工具:解决 VRChat 跨语言交流问题
  • GitHub Copilot 配置性能优化与开发环境调优指南
  • GESP 2023 年 12 月 C++ 二级认证试题解析(选择题 9-15)
  • Flutter shelf_web_socket 鸿蒙适配指南:端侧 WebSocket 服务构建
  • 算法性能优化实战策略:从瓶颈突破到效率提升
  • HDFS 核心组件深度解析:分布式文件系统架构基石

相关免费在线工具

  • 加密/解密文本

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