既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
{
RBTreeNode<K, V> _left;//结点的左孩子
RBTreeNode<K, V> _right;//结点的右孩子
RBTreeNode<K, V> _parent;//结点的双亲
pair<K, V>_kv;
Color _color;//该结点的颜色
RBTreeNode(const pair<K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_color(RED)
{}
};
## 红黑树的插入
>
> 那么我们插入结点时选择插入黑结点还是红结点呢?
>
>
>
当然是选择插入红结点了。选择插入黑结点那麻烦就大了,那1条路径上就多了1个黑结点,破坏了性质4,代价很大。插入红结点,如果它的父亲结点是黑色则不用调整,拍拍屁股走人,它的父亲是红色那我们在进行后序的处理。
**总结一下:**
1.插入黑色结点一定破坏性质4,调整起来会很麻烦
2.插入红结点不一定破坏红黑树的性质,它的父亲结点是红色才进行调整,比插入黑结点调整起来方便。
✨✨✨✨✨✨✨✨✨✨\*\*\*我是分割线\*\*\*✨✨✨✨✨✨✨✨✨✨✨✨✨✨
>
> 插入的逻辑:
>
>
>
1.找到插入结点的位置
2.插入结点
3.检测新结点插入后是否破坏了红黑的性质,如果破坏则需要进行处理
因为新插入结点的颜色是红色,若它的父亲结点是黑色不用调整,是红色的话需要对红黑树分情况来讨论。
>
> 红黑树调整主要看叔叔结点
>
>
>
下面我们根据叔叔结点的情况来具体看一下。
### 情况一
以下用p来代表parent结点,c代表cur为新增结点,g代表grandparent结点,u代表uncle结点。
我们还是跟AVL树一样画具象图:
1.叔叔结点存在且为红
![在这里插入图片描述](https://img-blog.csdnimg.cn/cd1b12cd21da42cab19655f0bc2e9e00.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
为什么把g变成红色呢?如果g不变成红色,那此时子树上就多了1个黑结点了。
只要我们画出具象图,那面试时手撕红黑树也完全不怂。
当然还有很多种情况,那就给出抽象图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/1032e74e2faf4c56822753db24a587f1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
这种情况下cur在p的左边还是右边都不影响。
### 情况二
>
> 叔叔结点存在且为黑,新增结点是p的左边
>
>
>
![在这里插入图片描述](https://img-blog.csdnimg.cn/06d4a34988bb45079b4c75fa3c8f5290.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
这是由情况一变来的,如果u存在那cur一定是黑的,不是新郑结点,这样才满足红黑树的性质。
✨✨✨✨✨✨✨✨✨✨\*\*\*我是分割线\*\*\*✨✨✨✨✨✨✨✨✨✨✨✨✨✨
>
> 新增结点是p的右边
>
>
>
![在这里插入图片描述](https://img-blog.csdnimg.cn/92109928e6344233ba37fe911f70b834.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
### 情况三
>
> 叔叔结点不存在
>
>
>
**新增结点在parent的左边**
![在这里插入图片描述](https://img-blog.csdnimg.cn/a578ee54753843b582642a808f1a968c.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
**新增结点在parent的右边**
![在这里插入图片描述](https://img-blog.csdnimg.cn/422073c114c646279c8835e641853d18.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
总结一下:
以上的情况都是父亲结点在祖先结点的左边,在祖先结点的右边也是相同的处理方法
1.叔叔结点存在且为红,把父亲结点和叔叔结点变黑,祖先变红继续向上处理直到祖先是根节点
2.叔叔存在为黑,祖孙三代在一条直线上进行单旋,不在则进行双旋
3.叔叔不存在,祖孙三代在一条直线上进行单旋,不在则进行双旋
所以2,3的逻辑可以合在一起,分为新增结点在父亲结点的左边还是右边处理。
**下面再来简单的说说父亲结点在祖先结点的右边**
>
> 叔叔存在且为红
>
>
>
![在这里插入图片描述](https://img-blog.csdnimg.cn/77971440be024919916fa148add7b9a1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
此时,cur在p的左边还是右边没有影响。
>
> 叔叔存在且为黑
>
>
>
**新增结点在父亲结点的右边**
![在这里插入图片描述](https://img-blog.csdnimg.cn/58c5fd873ca245759977a956e928550b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
**新增结点在父亲结点的左边**
![在这里插入图片描述](https://img-blog.csdnimg.cn/bad000af9df34109ba0230071c951fa9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
>
> 叔叔不存在
>
>
>
![在这里插入图片描述](https://img-blog.csdnimg.cn/49e86ae5c71240e9b79d9cb27fce9a27.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)
总结一下:
1.叔叔存在且为红,u,p变黑,g变红继续向上调,直到g为根结点,最后把g变黑
2.叔叔存在且为黑,祖孙3带在一条直线上单旋,折线要双旋
3.叔叔不存在,祖孙3带在一条直线上单旋,折线要双旋
✨✨✨✨✨✨✨✨✨✨\*\*\*我是分割线\*\*\*✨✨✨✨✨✨✨✨✨✨✨✨✨✨
代码如下:
pair<Node\*, bool> insert(const pair<K, V>& kv)
{
//1.树为空
if (_root == nullptr)
{
_root = new Node(kv);
_root->_color = BLACK;//根结点为黑色
return make\_pair(_root, true);
}
//树不为空
Node\* cur = _root;
Node\* parent = nullptr;
while (cur)
{
//新结点key大于当前结点往右边
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
//新结点key小于当前结点往左边
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return make\_pair(cur, false);
}
}
cur = new Node(kv);
Node\* newnode = cur;
newnode->_color = RED;
if (parent->_kv.first < kv.first)
{
parent->_right = newnode;
newnode->_parent = parent;
}
else
{
parent->_left = newnode;
newnode->_parent = parent;
}
//开始调整颜色
//父亲存在且为红
while (parent && parent->_color == RED)
{
Node\* grandParent = parent->_parent;
//parent是grandParent左孩子
if (grandParent->_left == parent)
{
Node\* uncle = grandParent->_right;
//叔叔存在且为红色,父亲和叔叔都调为黑色
//祖先调为红色,如果不调那每条路径的黑结点变了
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandParent->_color = RED;
//继续往上调
cur = grandParent;
parent = cur->_parent;
}
else//叔叔不存在或叔叔存在且为黑
{
if (parent->_left == cur)
{ //右单旋
RotateR(grandParent);
parent->_color = BLACK;
grandParent->_color = RED;
}
else //parent->\_right == cur
{
RotateL(parent);
RotateR(grandParent);
grandParent->_color = RED;
cur->_color = BLACK;
}
break;
}
}
else //parent是grandParent左孩子
{
Node\* uncle = grandParent->_left;
if (uncle && uncle->_color == RED)
{
uncle->_color = BLACK;
parent->_color = BLACK;
grandParent->_color = RED;
cur = grandParent;
parent = cur->_parent;
}
else
{
if (parent->_right == cur)
{
RotateL(grandParent);
parent->_color = BLACK;
grandParent->_color = RED;
}
else
{
RotateR(parent);
RotateL(grandParent);
cur->_color = BLACK;
grandParent->_color = RED;
}
break;
}
}
}
_root->_color = BLACK;
return make\_pair(newnode, true);
}
void RotateL(Node\* parent)
{
Node\* subR = parent->_right;
Node\* subRL = subR->_left;
Node\* parentParent = parent->_parent;
//先旋转
parent->_right = subRL;
subR->_left = parent;
parent->_parent = subR;
//在改父亲结点
if (subRL)
subRL->_parent = parent;
if (_root == parent)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
//subR旋转后可能是左右子树2种情况
if (parentParent->_left == parent)
parentParent->_left = subR;
else
parentParent->_right = subR;
subR->_parent = parentParent;
}
}
void RotateR(Node\* parent)
{
Node\* subL = parent->_left;
Node\* subLR = subL->_right;
Node\* parentParent = parent->_parent;//记录parent的父亲结点
//subLR做parent->\_left
parent->_left = subLR;
subL->_right = parent;
//同时更新动的2个节点的parent
//注意subLR还可能是空结点
if (subLR)
subLR->_parent = parent;
parent->_parent = subL;
//parent可能是单独的树,或者子树,分情况
if (_root == parent)
{
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
独的树,或者子树,分情况
if (_root == parent)
{
[外链图片转存中…(img-KmcmQAie-1715617960148)]
[外链图片转存中…(img-QaDBLnip-1715617960149)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!