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

C++ 红黑树:插入与平衡修复的直观理解

红黑树通过颜色和旋转维持近似平衡,插入新节点时默认染红以减少对黑色路径计数的破坏。修复逻辑围绕父节点颜色和叔叔节点状态展开,叔叔为红时变色上推,否则根据祖父-父-子位置旋转。验证时检查连续红节点和黑节点计数一致性。相比AVL树,红黑树以略高高度换取更少旋转,适合写密集型场景。

安卓系统发布于 2026/6/300 浏览
C++ 红黑树:插入与平衡修复的直观理解

红黑树是自平衡二叉查找树的一种,通过节点颜色和旋转维持近似平衡。实际用起来比 AVL 树省旋转,插入删除更轻量。

概念与性质

一棵合格的红黑树需要同时满足:

  • 每个节点要么红要么黑。
  • 根节点是黑色。
  • 红色节点的两个子节点必须黑色(没有连续红)。
  • 从任意节点到其后代所有叶子节点的简单路径上,黑色节点数量相同。
  • 叶子节点(空节点)视为黑色。

这些规则共同保证了树的高度不会超过 2log(n+1),也就是最长路径不会超过最短路径的两倍。直观理解:最短路径全黑,最长路径红黑交替。由于黑色节点数相等,红节点最多和黑节点一样多,所以最长路径最多是黑节点数的两倍,也就是最短路径的两倍。

节点结构及默认红色

enum Colour { RED, BLACK };

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

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

新节点默认染成红色,主要是为了减少对黑色路径计数的影响。如果插个黑节点,必然破坏'同路径黑节点数相等'的性质,修复起来很麻烦。红色节点只可能造成'连续红'冲突,处理起来大多只需变色或加少量旋转,调整成本更低。

插入过程

插入操作分为标准的 BST 插入和随后的红黑性质修复。

typedef RBTreeNode<K, V> Node;

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;
            
            if (uncle && uncle->_col == RED) {
                // 情况1:叔叔红色 -> 变色上推
                parent->_col = uncle->_col = BLACK;
                grandfather->_col = RED;
                cur = grandfather;
                parent = cur->_parent;
            } else {
                // 情况2/3:叔叔黑或无
                if (cur == parent->_left) {
                    // LL型,右旋祖父
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                } else {
                    // LR型,先左旋父再右旋祖父
                    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) {
                    // RR型
                    RotateL(grandfather);
                    grandfather->_col = RED;
                    parent->_col = BLACK;
                } else {
                    // RL型
                    RotateR(parent);
                    RotateL(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                break;
            }
        }
    }
    _root->_col = BLACK;
    return true;
}

插入后的修复循环围绕 parent 节点展开,只要父节点是红色就继续。核心在于区分叔叔节点的颜色:叔叔为红时,变色并把问题上移;叔叔为黑或不存在时,需要通过旋转调整父子祖三代的相对位置,并重新染色,调整后即可跳出循环。旋转的左右选择取决于从祖父到父再到当前节点的路径形状,画一下图会更直观——这也是实现红黑树时最需要细心的地方。

删除简述

红黑树的删除比插入复杂得多,这里不展开。如果真要用到,建议直接参考《算法导论》或成熟的库实现。

与 AVL 树的取舍

红黑树和 AVL 树都是自平衡二叉搜索树,但设计目标有差别。AVL 追求严格的平衡,每次插入删除后的旋转次数可能很多,适合读多写少的场景。红黑树允许稍微倾斜,换来更少的旋转开销,写入效率更高。实际工程中(比如 C++ STL 的 map、set)普遍使用红黑树,因为通用场景下写操作并不罕见。

验证代码

调试时自己写两个检查很管用:一个查有没有连续红节点,一个查每条路径的黑色节点数是否相等。

bool CheckColour(Node* root, int blacknum, int benchmark) {
    if (root == nullptr) {
        if (blacknum != benchmark)
            return false;
        return true;
    }
    if (root->_col == BLACK) {
        ++blacknum;
    }
    if (root->_col == RED && root->_parent && root->_parent->_col == RED) {
        cout << root->_kv.first << "出现连续红色节点" << endl;
        return false;
    }
    return CheckColour(root->_left, blacknum, benchmark) && CheckColour(root->_right, blacknum, benchmark);
}

bool IsBalance() {
    return IsBalance(_root);
}

bool IsBalance(Node* root) {
    if (root == nullptr)
        return true;
    if (root->_col != BLACK) {
        return false;
    }
    int benchmark = 0;
    Node* cur = _root;
    while (cur) {
        if (cur->_col == BLACK)
            ++benchmark;
        cur = cur->_left;
    }
    return CheckColour(root, 0, benchmark);
}

这两个函数组合使用就能快速定位插入/删除逻辑中的错误。个人经验是先把插入代码写完,然后用随机数据跑一遍,再调用 IsBalance() 检查,能省不少 debug 时间。

文章到这里主要讲了插入和验证,删除部分确实复杂,但理解插入的修复模式后,再去看删除会有帮助。

![图示:红黑树结构示意]

目录

  1. 概念与性质
  2. 节点结构及默认红色
  3. 插入过程
  4. 删除简述
  5. 与 AVL 树的取舍
  6. 验证代码
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • 在 Python 和 REST API 中使用 AutoGen Studio 导出的团队配置
  • 华为交换机初始配置实录:Console 登录、管理 IP 与 Web 网管开通
  • 大模型落地观察:金融、医疗、制造、物流和农业的真实进展
  • 从后端到前端:AI Agent 跨语言全栈项目实战记录(Java + Python + Vue3)
  • 前端首屏加载:六维优化实操清单
  • 在银河麒麟 V10 上使用 Docker 和 Compose 部署 .NET 8 WebAPI
  • 从零构建国产电视剧评分数据集:一个爬虫实战记录
  • WebSocket 客户端实践:重连、心跳与可靠通信
  • 鸿蒙平台集成 tavily_dart 进行 AI 语义搜索
  • 从零搭建一个能调用 API 的 AI Agent
  • 汉诺塔问题的递归与非递归 C++ 解法
  • 上手 Trae AI:从安装到实际项目的踩坑与心得
  • 7款国内AI助手横评:豆包、元宝、千问、Kimi、DeepSeek、MiniMax、GLM
  • 2026 算法求职:为什么我劝你深耕多模态大模型
  • 2023年网络安全趋势观察:十个绕不开的方向
  • Star-Office-UI:用像素办公室可视化AI工作状态
  • LangChain 实现零微调 Agent:从 Self Ask 到 ReAct
  • 用 MGeo 和 Neo4j 搭建中文地址语义知识图谱
  • Temperature 和 Top-P 调参手记:从输出翻车到稳定产出的经验
  • 一次GitHub学生认证的记录与避坑

相关免费在线工具

  • 加密/解密文本

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