目录:
-
红黑树概念
-
红黑树性质
-
红黑树的定义
-
红黑树的插入(重点)
- 情况1(违反原因、解决方法、整体图解)
- 情况2(情况的产生、解决方法、整体图解、方法判断)
- 情况3(方法判断、解决方法、整体图解)
- 三种情况的总结
-
红黑树的代码实现
讲前小结:红黑树的插入部分理解的时候要多画图,搞清楚不同情况下的插入逻辑。其实画图花多了,理解起来就不是那么的费劲。
本节大部分是图解。红黑树中的红色节点用红色代替,黑色节点用黑色代替
红黑树
红黑树的概念
红黑树,其实就是一种二叉搜索树的优化。每一个节点都会有一个颜色进行标记(红色,或者黑色)。它的五条性质决定它是一种近似平衡的二叉树。
红黑树的性质:
- 每各节点不是黑色就是红色
- 根节点是黑色
- 如果一个节点是红色的,则它的两个孩子节点是黑色的
- 对于每个节点,从节点到其所有后代叶节点的路径上,包含相同数目的黑色节点
- 每个叶子节点都是黑色的(叶子节点是指空节点)
我们根据上面五条性质,可以知道红黑树中最主要的性质是:
- 红黑树中不会出现连续的红节点(第3条性质)
- 每一条路径上的黑色节点的数量相同(第4条性质)
还能由这五条性质可以推出:
红黑树中最长路径中节点个数不会超过最短路径节点个数的两倍
假设每一条路径上有X个黑色节点,红黑树中红色节点,不能连续出现,所以可以穿插在黑色节点,肯定不会破坏性质4. 那么最长的路径就是2x-1. 2x - 1 < 2*x 。所以最长路径中节点的个数不会超过最短路径中节点个数的两倍。
红黑树节点的定义
红黑树是三叉链型的结构。所以每一个节点中都要有三个指针,指向左右子节点和父亲节点
enum Colour //标记红黑树的节点的颜色
{
BLACK,
RED,
};
// 红黑的节点
template <class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data; // 节点的值
Colour _col; // 节点的颜色
RBTreeNode(const T& data) //节点的构造函数
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{
}
};
红黑树中最重要的步骤就是红黑树的插入
红黑树的插入
红黑树是对二叉搜索数的优化,所以插入节点的要求还是按照二叉搜索树的,小于节点的,插入到左子树,大于节点的,插入到右子树
红黑树中由于有五条性质限制,所以我们要考虑插入节点是否破坏了当前的红黑树,而且对于破坏结构的红黑树,还要用 调整红黑树中节点的颜色 和旋转的方法适当调整红黑树,使红黑树满足五条性质。
我们在插入的时候插入节点选择红色还是黑色?
我们插入的新节点也应该尽量的不破坏红黑树的五条性质。所以我们插入的新节点默认颜色是红色。如果我们插入的节点是黑色,则一定会破坏性质4,使得每一条路径上黑色节点的数目不一样多。 如果我们插入的节点是红色,我们有可能破坏性质3,如果父节点也是红色的,那么我们就对红黑树进行调节。所以插入的时候,默认新节点的颜色是红色
但是插入之后,就有可能违反红黑树的五条性质,破坏红黑树的结构。所以我们要分情况来讨论。
总的特殊情况分为三类
约定cur为当前节点,p为父节点(parent),g为祖父节点(grandparent),u为叔叔节点(uncle)
违反原因:
插入的cur为红色节点,parent节点也是红色,两个红色节点连在一起,违反了第三条性质,破坏了原本的红黑树结构。
解决方法:
将parent节点和uncle节点改成黑色,grandfather节点改为红色。然后把grandfather当成cur,继续向上调整
如下图(cur插入在parent的左右 或者 parent和uncle互换左右都是一样的)
整体图解:
注意 :grandparent节点不是根节点,但是当grandparent的上一个节点还是红节点的话,还得继续向上调整。
情况二的产生
根据uncle节点的有无,cur节点的又有两种不同的解释(解决的方案都一样,而且弄懂逻辑就很简单了)
- 当uncle节点不存在时,则cur一定是新插入的节点,因为每一条路径上的黑色节点个数要相同
- 当uncle节点存在且黑为色是,cur节点的原来的颜色一定是为黑色的。红色的原因是cur的子树在调整的过程正将cur节点的颜色由黑色改为红色。
解决方法:
parent为grandparen的左孩子,cur为p的左孩子,则进行右单旋
parent为grandparent的右孩子,cur为p的右孩子,则进行左单旋
parent变成黑色
grandparent变为红色。
整体图解:
情况二判断旋转的方法:
在情况二中,我们可以看到
- cur、parent、grandparent都是在一条线上的。
- 左边高,将左边压下来,所以进行右单旋。
- 右边高,将右边压下来,所以进行左单旋
例如