跨境派

跨境派

跨境派,专注跨境行业新闻资讯、跨境电商知识分享!

当前位置:首页 > 卖家故事 > 【C++进阶06】红黑树图文详解及C++模拟实现红黑树

【C++进阶06】红黑树图文详解及C++模拟实现红黑树

时间:2024-03-25 10:26:33 来源:网络cs 作者:欧阳逸 栏目:卖家故事 阅读:

标签: 实现  模拟 
阅读本书更多章节>>>>

在这里插入图片描述

一、红黑树的概念及性质

1.1 红黑树的概念

AVL树用平衡因子让树达到高度平衡
红黑树可以认为是AVL树的改良
通过给每个节点标记颜色让树接近平衡
以减少树在插入节点的旋转
在每个结点新增一个存储位表示结点颜色
可以是Red或Black
通过对任何一条从根到叶子的路径上
各个结点着色方式的限制
红黑树确保没有一条路径会比其他路径长出
俩倍,因而是接近平衡的

1.2 红黑树的性质

每个结点不是红色就是黑色根节点是黑色的如果一个节点是红色的
则它的两个孩子结点是黑色的对于每个结点
从该结点到其所有后代叶结点的简单路径上
均包含相同数目的黑色结点每个叶子结点都是黑色的
(此处的叶子结点指的是空结点)

在这里插入图片描述

为啥满足上面性质的红黑树就能保证
其最长路径节点个数不会超过最短路径
节点个数的两倍?

由性质3可得出不能出现连续红色节点
由性质4可得出每条路径有相同黑色节点数量

极限情况下
最短路径:全黑
最长路径:一黑一红

由此可得出
最长路径不会超过最短路径的两倍
在这里插入图片描述

1.3 为什么更常用红黑树而不是AVL树?

AVL树: 是一颗高度平衡的二叉树
查找效率: O ( l o g N ) O(logN) O(logN)
但是这样的效率是在插入元素时
经常性的旋转换来的

红黑树: 是一颗接近平衡的二叉树
假设全部黑节点有N个
整棵树的节点数量:[N, 2N]之间
最短路径长度: O ( l o g N ) O(logN) O(logN)
最长路径长度: O ( 2 l o g N ) O(2logN) O(2logN)
查找效率: O ( 2 l o g N ) O(2logN) O(2logN)

10亿数据AVL树最多查找30次
红黑树最多也就查找60次
对于cpu的运行速度来说几乎可以忽略不计
但红黑树的规则相对于AVL树没那么严格
在插入元素时,不会经常旋转
所以综合而言红黑树更胜一筹

如图: 对于AVL树必定旋转
红黑树则不用
在这里插入图片描述

二、红黑树模拟实现的基本框架

2.1 红黑树节点的定义

跟AVL树一样
只是AVL树采用平衡因子
让树达到平衡
而红黑树对节点进行颜色标记
让树达到平衡
定义一个枚举表示节点颜色

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){}};template <class K, class V>class RBTree{typedef RBTreeNode<K, V> Node;public:private:Node* _root = nullptr;};

2.2 红黑树的插入

还是和AVL树一样

bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);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);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}// new的节点的parent还指向空cur->_parent = parent;// 插入黑色节点还是红色节点?return true;}

插入走到这里如果是AVL树
此时需要更新平衡因子

红黑树采用的是标记节点颜色
让树达到平衡
需要考虑的是插入什么颜色的节点?

插入黑色节点
会违反规则4,影响到每条路径插入红色节点
如果插入节点的父节点也是红色节点
则会违反规则3影响当前局部节点

很明显插入红色节点更划算
所有插入的节点都默认是红色
如果违反红黑树的规则,再进行调整

三、对插入节点调整的解析

如果插入节点的父节点为黑
则无需处理
如果为红,则分为三种情况

情况一:

cur为红,p为红,g为黑,u存在且为红

cur为当前节点,p为父节点
g为祖父节点,u为叔叔节点
在这里插入图片描述
把p和u变黑,g变红
在这里插入图片描述
如果grandfather的parent也为红
把grandfather改为cur
继续按刚才的步骤往后迭代
在这里插入图片描述
如果grandfather为根节点
把grandfather改为黑色
颜色调整结束
在这里插入图片描述

情况二:

cur为红,p为红,g为黑
u不存在或u存在且为黑

此树可能是完整树也可能是子树
u节点不存在
在这里插入图片描述
p为g的左孩子,cur为p的左孩子
则进行右单旋转
相反
p为g的右孩子,cur为p的右孩子
则进行左单旋转
p、g变色–p变黑,g变红
在这里插入图片描述

在这里插入图片描述
下图则是u节点存在的情况
在这里插入图片描述
c为下面4种情况的
任意一种包含一个黑节点的红黑树
d和e可能是空或者一个红节点
在这里插入图片描述
插入新节点,更新完后
继续往后更新
就是情况二的u存在的情况
在这里插入图片描述

情况三:

cur为红,p为红,g为黑
u不存在或u存在且为黑

跟情况二完全类似
只是情况三为双旋
情况二是单旋

p为g的左孩子,cur为p的右孩子
则针对p做左单旋转
相反
p为g的右孩子,cur为p的左孩子
则针对p做右单旋转

则转换成了情况2
此图为u不存在
u存在参考情况二
在这里插入图片描述

四、红黑树插入代码的全部实现

详解都在代码注释
各位友友们请耐心看完

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);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}// new的节点的parent还指向空cur->_parent = parent;// 如果新插入节点破坏了红黑树规则// 则更新节点颜色while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;// 情况1:u存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3:u不存在或者u存在且为黑,旋转+处理{// 如果插入节点在父节点的左,c、p、g呈一条斜线//     g//   p   u// cif (parent->_left == cur){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED; }else{// 插入节点在父节点的右,c、p、g呈一条折线//      g//   p      u//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;// parent->_col = RED; // 父亲本就是红,变一下双重保险grandfather->_col = RED;}break;}}else // (grandfather->_right == parent){Node* uncle = grandfather->_left;// 情况1:u存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2+3:u不存在或者u存在且为黑,旋转+处理{//   g// u    p//         cif (parent->_right == cur){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{//   g// u     p//     cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK; // 做个双保险,无论那种情况把根都变成黑的return true;}

五、红黑树全部代码模拟实现

gitee链接

✨✨✨✨✨✨✨✨
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见
✨✨✨✨✨✨✨✨

阅读本书更多章节>>>>

本文链接:https://www.kjpai.cn/gushi/2024-03-25/148250.html,文章来源:网络cs,作者:欧阳逸,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。

文章评论