当前位置: 首页 > news >正文

封装map和set所需第二步:红黑树

1.红黑树的规则

1.每个结点只有黑或红色

2.根结点只为黑色

3.红节点的两孩子只为黑色

4.所有节点的绝对路径的黑色节点数都相同(绝对路径:从根节点到每一个子节点的过程)

2.红黑树的一些特点

1.设每一条路径有x个黑色结点,有最短路径为x,最长路径为x*2。

下图为最长:

下图为最短:

设N为该树的所有节点数,x为最短路径,有2^x-1 <=N<=2^(x+1)-1.即n=logN.可得一次查找的效率还是为O(logN).

下面开始代码实现:
1.结点和大框

//枚举封装颜色 enum { RED, BLACK }; template<class K, class V> struct RBTreeNode { RBTreeNode* _left; RBTreeNode* _right; RBTreeNode* _parent; pair<K, V> _val; int _col; RBTree(const pair<K, V>& val) :_val(val) ,_left(nullptr) ,_right(nullptr) ,_parent(nullptr) //结点颜色优先为红色 //因为违反条件4的代价很大 ,_col(RED) { } }; template<class K, class V> class RBTree { public: typedef RBTreeNode<K, V> Node; private: Node* _root = nullptr; };

2.出现矛盾时的修改

如上图出现矛盾时,我们将新插的节点为cur,其父亲为parent,父亲的父亲为grandparent,grandparent的另一个孩子为uncle。

当cur与parent的颜色出现矛盾时,grandparent的颜色一定为黑色,此时uncle的颜色就极为重要。

1.uncle为红色

解法:

(cur可能是新节点也可能是从下面更新出来的)

这些情况下只需将parent和uncle更新为black,grandparent更新为red。然后将cur去到grandparent处向上更新即可。(可能grandparent为红后与其的祖父又发生了矛盾)

代码:(说白了,红黑树与AVL树的差别只有insert是不同的)

bool insert(const pair<K, V>& val) { if (_root == nullptr) { _root = new Node(val); _root->_col = BLACK; return true; } Node* cur = _root; Node* parent = nullptr; while (cur) { if (cur->_val.first < val.first) { parent = cur; cur = cur->_right; } else if (cur->_val.first > val.first) { parent = cur; cur = cur->_left; } else { return false; } } //此处走到空结点 cur = new Node(val); //连接parent和cur if (parent->_val.first < val.first) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent; //parent==nulptr代表走到_root了 while (parent && parent->_col == RED) { Node* grandparent = parent->_parent; //这里核心是用于限定uncle在grandparent的左还是右的 if (grandparent->_left == parent) { Node* uncle = grandparent->_right; //uncle==nullptr为其它情况 if (uncle && uncle->_col == RED) { uncle->_col = parent->_col = BLACK; grandparent->_col = RED; cur = grandparent; parent = grandparent->_parent; } else { if (parent->_left == cur) { rotateR(grandparent); parent->_col = BLACK; grandparent->_col = RED; } else { rotateL(parent); rotateR(grandparent); cur->_col = BLACK; grandparent->_col = RED; } //修改完后就要break了,不要再往上走了 break; } } else { Node* uncle = grandparent->_left; //uncle==nullptr为其它情况 if (uncle && uncle->_col == RED) { uncle->_col = parent->_col = BLACK; grandparent->_col = RED; cur = grandparent; parent = grandparent->_parent; } else { if (parent->_left == cur) { rotateR(parent); rotateL(grandparent); cur->_col = BLACK; grandparent->_col = RED; } else { rotateL(grandparent); parent->_col = BLACK; grandparent->_col = RED; } break; } } } _root->_col = BLACK; return true; }

2.uncle为黑或uncle为空

1.uncle为空

此时cur一定为新插入结点,不然左边一定有别的黑节点使黑节点数两边不相等。

2.uncle为黑

此时cur一定不为新插入结点,不然左子树一定比右子树少至少一个黑节点。

二者本质是一样的,只有旋转才能解决,此时又分两种情况。

1.cur在parent的左子树,右单旋

以grandparent为旋转点单右旋,然后将grandparent改为红色,parent改为黑色。

2.cur在parent的右子树,左右双旋

先以parent为旋转点单左旋,再以grandparent为旋转点单右旋,grandparent改为红色,cur改为黑色.

代码:

while (parent && parent->_col == RED) { Node* grandparent = parent->_parent; //这里核心是用于限定uncle在grandparent的左还是右的 //此处uncle为右 if (grandparent->_left == father) { Node* uncle = grandparent->_right; //uncle==nullptr为其它情况 if (uncle&&uncle->_col == RED) { uncle->_col = parent->_col = BLACK; grandparent->_col = RED; cur = grandparent; parent = grandparent->_parent; } else if (parent->_left == cur) { rotateR(grandparent); parent->_col = BLACK; grandparent->_col = RED; } else { rotateL(parent); rotateR(grandparent); cur->_col = BLACK; grandparent->_col = RED; } } //此处uncle为左 else { Node* uncle = grandparent->_laft; //uncle==nullptr为其它情况 if (uncle && uncle->_col == RED) { uncle->_col = parent->_col = BLACK; grandparent->_col = RED; cur = grandparent; parent = grandparent->_parent; } else if (parent->_left == cur) { rotateR(parent); rotateL(grandparent); cur->_col = BLACK; grandparent->_col = RED; } else { rotateL(parent); parent->_col = BLACK; grandparent->_col = RED; } }

3.检查是否为红黑树

1.用孩子找是否与父亲都为红色

2.计算每一条路径的黑色结点是否都相同

bool isRBtree() { if (_root == nullptr) return true; if (_root->_col == RED) return false; Node* cur = _root; int realheight = 0; while (cur) { if (cur->_col == BLACK)realheight++; //随便找一条路径的黑色节点数即可 cur = cur->_left; } return _isRBtree(_root, 0, realheight); } private: //num是形参,找每一条的黑色结点个数,realnum是外部传的一个一条路径的黑色节点数 bool _isRBtree(Node* _root, int num, const int realnum) { //说明遍历到头了 if (_root == nullptr) { if (num != realnum) { cout << "not real number" << endl; return false; } return true; } if (_root->_col == BLACK) num++; if (_root->_col == RED && _root->_parent->_col == RED) { cout << "contrast colour " << endl; return false; } return _isRBtree(_root->_left, num, realnum) && _isRBtree(_root->_right, num, realnum); }

总结两次出的问题都在于在更新完后没有及时的break,因此要注意在调整更新后要及时地break,部分情况除外。

http://www.jsqmd.com/news/563179/

相关文章:

  • 3步掌握SillyTavern:从零构建AI角色对话系统的终极指南
  • Suspense 异步组件与懒加载实战
  • 实测STM32L053待机功耗65uA,手把手教你配置唤醒引脚(附完整代码)
  • 解决打印机标签尺寸匹配问题
  • C++并发编程实战:std::atomic的exchange与compare_exchange操作到底怎么选?
  • GStreamer 核心组件解析:Element 的创建、连接与 Pipeline 构建实战
  • Windows下利用Rclone实现多协议云存储盘符映射实战指南
  • 如何为Umi-OCR选择最适合的离线文字识别插件?
  • 3 分钟速算!UPS后备时间简易估算方法
  • 二叉树必刷 2 题|中序遍历(统一迭代防溢出)+ 最大深度(极简递归)
  • 从MWS到SP-API:Java开发者如何平滑过渡亚马逊新接口
  • 5分钟搞定!用Keil MDK将STM32F103C8T6工程无缝迁移到ZET6开发板
  • 学浪视频下载终极方案:Fiddler+N_m3u8D联动配置避坑指南
  • 仅剩最后3家银行未完成Java Istio全面替换——这份含12类Java Agent冲突检测脚本、4种Sidecar注入模式对比的适配手册即将下线
  • 新电脑装Node 22,pnpm install就报ERR_INVALID_THIS?一个版本锁死的教训
  • OCS2与Pinocchio联调避坑指南:如何让机械臂MPC求解速度提升3倍?
  • proxy_pass 路径拼接
  • 终极指南:3步快速搭建AI驱动的Claude应用开发环境
  • 保姆级教程:手把手教你本地部署Qwen2.5-7B-Instruct旗舰模型
  • 深入解析dlopen:动态库加载的机制与实践
  • 用Python和LSB算法给你的图片藏点小秘密:一个完整可用的隐写脚本(附PSNR分析)
  • nginx之反向代理与路径重写配置
  • 揭秘 Qt 信号与槽机制的高效实现原理
  • 2026冷排管回收行业白皮书合规处理解析:风冷系统回收/食品车间拆除/cnc铣床回收/smc气动设备回收/选择指南 - 优质品牌商家
  • Cyber Engine Tweaks:解锁《赛博朋克2077》终极模组开发能力的5大核心功能 [特殊字符]
  • Swagger2Word终极指南:从Swagger文档到专业Word接口文档的高效转换方案
  • 华为eNSP实战:5分钟搞定跨交换机VLAN通信(附Trunk配置避坑指南)
  • LangChain工具绑定避坑指南:为什么你的bind_tools不工作?
  • 解锁Nvidia Tesla A100完整性能:从驱动安装到Fabric Manager服务配置
  • LedBlink:嵌入式LED可编程闪烁控制轻量框架