目录
一、红黑树概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。红黑树就能保证其最长路径中节点个数不会超过最短路径节点个数的两倍。
满足性质:
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点
二、代码实现
1 节点定义
enum Color { RED, BLACK };
RBTreeNode(const ValueType& data = ValueType())
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _color(RED)
{}
RBTreeNode<ValueType>* _pLeft; // 节点的左孩子
RBTreeNode<ValueType>* _pRight; // 节点的右孩子
RBTreeNode<ValueType>* _pParent; // 节点的双亲
ValueType _data; // 节点的值域
Color _color; // 节点的颜色
2 树的定义
template<class ValueType>
class RBTree
{
typedef RBTreeNode<ValueType> Node;
public:
RBTree()
:_root(nullptr)
{}
bool Insert(const ValueType& data);
bool IsBalance();
private:
bool _isBanlance(Node* cur,int banchmark,int blacknum);
void RotateL(Node* parent);
void RotateR(Node* parent);
private:
RBTreeNode<ValueType>* _root;
};
3 插入
在这里将节点按下图的方式命名,p表示当前节点的父亲节点,u表示叔叔节点,g表示祖父节点。插入节点默认为红节点,如果插入节点的父节点是黑,则红黑树规则不会被破坏,无需任何操作。据此,将节点插入分为两种需要操作的情况,情况一:u节点存在且为红,情况二:u节点不存在或存在且为黑。
下图是p节点在左,p节点在右的情况是完全对称的,就不在区分出来分析。
3.1 情况一:u节点存在且为红
将p、u改为黑,g改为红。
如果g是根节点,则最后g改为黑。如果g是子树的根节点,且该节点的父节点还是红机点,则g是新的cur节点向上判断。
3.2 情况二:u节点不存在或存在且为黑。
该情况下二叉树需要旋转。旋转规则跟AVL树类似。又可细分两个情况,cur在p左插入,cur在p右插入。第一种需要g右旋,p改为黑,g改为红,第二种情况先对p左旋就变成了第一种情况。
3.3 代码实现部分
bool Insert(const ValueType& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_color = BLACK;
return true;
}
//找到插入节点位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_data < data)
{
parent = cur;
cur = cur->_pRight;
}
else if (cur->_data > data)
{
parent = cur;
cur = cur->_pLeft;
}
else
{
return false;
}
}
//插入节点
cur = new Node(data);
if (parent->_data > data)
parent->_pLeft = cur;
else if (parent->_data < data)
parent->_pRight = cur;
else
assert(false);
cur->_pParent = parent;
//控制平衡
while (parent && parent->_color == RED)
{
Node* grandfather = parent->_pParent;
if (parent == grandfather->_pLeft)//父节点在左
{
Node* uncle = grandfather->_pRight;
//uncle存在切为红,往上变色
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = grandfather->_pParent;
}
//uncle不存在或者存在为黑,需要旋转
else
{
if (cur == parent->_pLeft)//需要单旋
{
RotateR(grandfather);
grandfather->_color = RED;
parent->_color = BLACK;
}
else//需要双旋
{
RotateL(parent);
RotateR(grandfather);
grandfather->_color = RED;
cur->_color = BLACK;
}
}
}
else
{
Node* uncle = grandfather->_pLeft;
//uncle存在切为红,往上变色
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = grandfather->_pParent;
}
//uncle不存在或者存在为黑,需要旋转
else
{
if (cur == parent->_pRight)//需要单旋
{
RotateL(grandfather);
grandfather->_color = RED;
parent->_color = BLACK;
}
else//需要双旋
{
RotateR(parent);
RotateL(grandfather);
grandfather->_color = RED;
cur->_color = BLACK;
}
}
}
}
_root->_color = BLACK;
return true;
}
void RotateL(Node* parent)
{
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
parent->_pRight = subRL;
if (subRL)
{
subRL->_pParent = parent;
}
Node* parentParent = parent->_pParent;
subR->_pLeft = parent;
parent->_pParent = subR;
if (_root == parent)
{
_root = subR;
subR->_pParent = nullptr;
}
else
{
if (parentParent->_pLeft == parent)
parentParent->_pLeft = subR;
else
parentParent->_pRight = subR;
subR->_pParent = parentParent;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_pLeft;
Node* subLR = subL->_pRight;
parent->_pLeft = subLR;
if (subLR)
subLR->_pParent = parent;
Node* parentParent = parent->_pParent;
subL->_pRight = parent;
parent->_pParent = subL;
if (parent == _root)
{
_root = subL;
_root->_pParent = nullptr;
}
else
{
if (parentParent->_pLeft == parent)
parentParent->_pLeft = subL;
else
parentParent->_pRight = subL;
subL->_pParent = parentParent;
}
}
4 验证
首先验证根节点必须是黑色。遍历最左路径,找到一条路径上的黑色节点数作为参考值。通过递归的方法记录每条路径的黑色节点数,并与参考值比较。同时在遍历路径的时候检查是否出现连续的红色节点。
bool _isBanlance(Node* cur,int banchmark,int blacknum)
{
if (cur == nullptr)
{
if (banchmark == blacknum)
return true;
std::cout << "路径黑色节点数不等" << std::endl;
return false;
}
if (cur->_color == RED && cur->_pParent->_color == RED)
{
std::cout << "出现连续红色节点" << std::endl;
return false;
}
if (cur->_color == BLACK)
blacknum++;
return _isBanlance(cur->_pLeft, banchmark, blacknum)
&& _isBanlance(cur->_pRight, banchmark, blacknum);
}
bool IsBalance()
{
if (_root && _root->_color == RED)
{
std::cout << "根节点是红色" << std::endl;
return false;
}
int banchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_color == BLACK)
banchmark++;
cur = cur->_pLeft;
}
int blacknum = 0;
return _isBanlance(_root,banchmark,blacknum);
}