一、什么是红黑树?
红黑树是一种二叉搜索树,但是每个结点上都增加一个颜色,可以是红色或者黑色;通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍(相对于二叉搜索树条件弱化了),因而接近平衡;
二、红黑树的性质
(1)每个结点不是红色就是黑色;
(2)根节点是黑色的;
(3)若一个结点是红的那么它的两个孩子结点都是黑色的;
(4)每个结点,从该结点到其他所有后代叶节点的路上,所包含的黑色结点数目是相同的;
(5)每个叶子节点都是黑的;
三、红黑树结点的定义和结构
1、红黑树的结构
(1)红黑树实现中添加了头结点,因为根节点必须为黑色,为了与根节点进行区分,将头结点也给成黑色;
(2)头结点的双亲指针指向红黑树的根节点, 左孩子指向红黑树中最小的结点,右孩子指向红黑树中最大的结点;
2、红黑树结点的定义
// 节点的颜色
enum Color{RED, BLACK};
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{
RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _color(color)
{}
RBTreeNode<ValueType>* _pLeft; // 节点的左孩子
RBTreeNode<ValueType>* _pRight; // 节点的右孩子
RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)
ValueType _data; // 节点的值域
Color _color; // 节点的颜色
为什么要将结点的默认颜色设为红色?
插入红色节点树的性质可能不会改变,而插入黑色节点每次都会违反性质4,故将结点设为红色可以减少认为对红黑树的影响;
三、红黑树的操作
1、插入
(1)按照二叉搜索树的规则插入新的结点;
(2)检测插入新结点后,红黑树的性质是否遭到了破坏;
遭到破坏的情况:
(其中cur为当前结点,p为父节点,g为祖父节点,u为叔叔结点)
a)cur为红,p为红,g为黑,u存在且为红:(cur一定为新增店)
把p、u改为黑,g改为红,然后把g当成cur继续向上调整;
b)cur为红,p为红,g为黑,u不存在/u为黑;
(若u存在且为黑,那么cur一定不是新增的)
解决方法:进行右单旋,p变黑,g变红;
同理如果是在右子树的右边插入破坏了上述规则,进行左单旋即可,并进行变色;
c)cur为红,p为红,g为黑**,u不存在/u为黑;**
解决方法:
对其进行左单旋,变成了情况b),进行右单旋;
其他情况适当取反,若是在g的右孩子的左边插入,则对其进行右旋,回到情况b);
注:上述情景只是一个抽象的情况,而不是一个具象的情况;
2、验证
(1)验证其是否满足二叉搜索树(中序遍历是否为有序序列)
(2)检测其是否满足红黑树的性质;
红黑树实现代码:
https://github.com/Wilingpz/sunny/tree/master/%E7%BA%A2%E9%BB%91%E6%A0%91
四、红黑树和AVL树的比较
(1)红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是o(log以二为底的N)
(2)红黑树不追求绝对的平衡,只需要保证最长路径不超过最短路径的两倍即可;AVL树确保任何一个节点的左右子树的高度差的绝对值不大于1即可;
(3)相对于AVL树,红黑树降低了插入和旋转的次数,所以在增删过程中的性能优于AVL树,且红黑树的实现比较简单;
五、利用红黑树模拟实现map和set
红黑树中的迭代器
- begin()和end();
将begin()放在红黑树中最小结点(最左侧结点)的位置(红黑树的中序遍历会得到一个有序的序列),end()放在头结点位置;
-end()无法找到最大结点的下一个位置是在哪里,所以将end()的位置放在头结点处。
static RBTreeNode<T> * increasement(RBTreeNode<T> * cur)//升序
{
RBTreeNode<T> * tmp = cur;
if (cur->m_right)
{//找cur右子树的最左子树
tmp = cur->m_right;
for (; tmp->m_left; tmp = tmp->m_left);
}
else//cur的右子树不存在,直到找到cur!=parent->right
{
tmp = tmp->m_parent;
for (; cur != tmp->m_left && cur == tmp->m_right; tmp = tmp->m_parent)
{
cur = tmp;
}
}
return tmp;//返回离cur最近的双亲结点(让cur作为左子树的结点)
}
static RBTreeNode<T> * decreasement(RBTreeNode<T> * cur)
{
RBTreeNode<T> * tmp = cur;
if (cur->m_left)//cur的左子树存在
{
//找cur左子树的最右子树
tmp = cur->m_left;
for (; tmp->m_right; tmp = tmp->m_right);
}
else//直到找到cur!=parent->left
{
tmp = tmp->m_parent;
for (; cur != tmp->m_right && cur == tmp->m_left; tmp = tmp->m_parent)
{
cur = tmp;
}
}
return tmp;
}
class iterator
{
RBTreeNode<T> * m_ptr;
public:
iterator(RBTreeNode<T> * val = nullptr) :
m_ptr(val)
{}
iterator operator++()
{
m_ptr = increasement(m_ptr);
return *this;
}
iterator operator++(int)
{
iterator tmp = *this;
m_ptr = increasement(m_ptr);
return tmp;
}
iterator operator--()
{
m_ptr = decreasement(m_ptr);
return *this;
}
iterator operator--(int)
{
iterator tmp = *this;
m_ptr = decreasement(m_ptr);
return tmp;
}
T & operator*()
{
return m_ptr->m_data;
}
RBTreeNode<T> * operator->()
{
return m_ptr;
}
bool operator==(const iterator & val) const
{
return m_ptr == val.m_ptr;
}
bool operator!=(const iterator & val) const
{
return m_ptr != val.m_ptr;
}
};
iterator begin()
{
return m_head->m_left;
}
iterator end()
{
return m_head;
}
};