1.红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
2.性质
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?
最短路径节点全黑,最长路径节点红黑交替(红不能连续出现)并且每条路径黑色节点数量相同,因此最长路径中节点数最多是最短路径节点数的两倍。
3.节点定义
// 节点的颜色
enum Color { RED, BLACK };
// 红黑树节点的定义
template<class T>
struct RBTreeNode
{
RBTreeNode(const T& data = T(),Color color = RED)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _color(color)
{}
RBTreeNode<T>* _pLeft;
RBTreeNode<T>* _pRight;
RBTreeNode<T>* _pParent;
T _data;
Color _color;
};
在节点的定义中,为什么要将节点的默认颜色给成红色的?
在节点插入时,若每次插入黑色节点,则每次都违反性质4,而插入红色节点,红黑树的性质可能不会改变,所以将节点设置为红色在插入节点时对红黑树造成的影响最小。
4.插入
红黑树节点插入时,性质三和性质四,我们实现插入时,就是为了维护性质3、4,在不同的情况下可以通过变色、变色+旋转去维护。
4.1 情况1(变色)
情况1:cur为红,p为红,g为黑,u存在且为红
当cur为新增节点时出现情况1:
当向上层变色时出现情况1:
变化到第3步后再判断g是否为根节点然后向上层处理。
情况1单纯通过变色就能维护红黑树的性质。
4.2 情况2(变色+单旋)
情况2:cur为红,p为红,g为黑,u不存在/u存在且为黑
当p为g的左孩子,cur为p的左孩子时,构成LL型,进行右单旋
当p为g的右孩子,cur为p的右孩子时,构成RR型,进行左单旋
下面演示其中一种
u不存在时:
u存在且为黑(这种情况不会在插入的地方出现,是在向上层变色中出现的):
4.3 情况3(变色+双旋)
情况3:cur为红,p为红,g为黑,u不存在/u存在且为黑
p为g的左孩子,cur为p的右孩子,构成LR型,右左双旋。
p为g的右孩子,cur为p的左孩子,构成RL型,左右双旋。
u不存在:
u存在且为黑:
一次单旋加换位后与情况2相同
为了后续封装map和set能更简单,这里实现红黑树时加入了一个头节点。
插入代码如下:
bool Insert(const T& data)
{
Node*& pRoot = GetRoot();
//将根节点设置为黑色
if (nullptr == pRoot)
{
pRoot = new Node(data, BLACK);
pRoot->_pParent = _pHead;
return true;
}
else
{
Node* pCur = pRoot;
Node* pParent = nullptr;
//找到插入节点的位置
while (pCur)
{
pParent = pCur;
if (data < pCur->_data)
pCur = pCur->_pLeft;
else if (data > pCur->_data)
pCur = pCur->_pRight;
else
return false;
}
//插入节点
pCur = new Node(data, RED);
if (data < pParent->_data)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
pCur->_pParent = pParent;
//讨论刚才的几种情况
while (pParent != _pHead && pParent->_color == RED)
{
Node* g = pParent->_pParent;
if (pParent == g->_pLeft)
{
Node* u = g->_pRight;
//u存在且为红
if (u && RED == u->_color)
{
//情况1
pParent->_color = BLACK;
u->_color = BLACK;
g->_color = RED;
pCur = g;
pParent = pCur->_pParent;
}
else
{
//u不存在或u为黑
if (pCur == pParent->_pRight)
{
//情况3
RotateL(pParent);
swap(pCur, pParent);
}
//情况2
pParent->_color = BLACK;
g->_color = RED;
RotateR(g);
}
}
else
{
//一二三的反情况
Node* u = g->_pLeft;
if (u && RED == u->_color)
{
pParent->_color = BLACK;
u->_color = BLACK;
g->_color = RED;
pCur = g;
pParent = pCur->_pParent;
}
else
{
//u不存在或u为黑
if (pCur == pParent->_pLeft)
{
//情况3的反情况
RotateR(pParent);
swap(pCur, pParent);
}
//情况2的反情况
pParent->_color = BLACK;
g->_color = RED;
RotateL(g);
}
}
}
}
pRoot->_color = BLACK;
_pHead->_pLeft = LeftMost();
_pHead->_pRight = RightMost();
return true;
}
5.红黑树的验证
红黑树的检测分为两步:
- 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
- 检测其是否满足红黑树的性质
bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack)
{
if (nullptr == pRoot)
return true;
if (pRoot->_color == BLACK)
pathBlack++;
Node* pParent = pRoot->_pParent;
if (pParent != _pHead && RED == pParent->_color && RED == pRoot->_color)
{
cout << "违反性质3:不能存在连在一起的红色节点" << endl;
return false;
}
if (nullptr == pRoot->_pLeft && nullptr == pRoot->_pRight)
{
if (pathBlack != blackCount)
{
cout << "违反性质4:每条路径中黑色节点数相同" << endl;
return false;
}
}
return _IsValidRBTRee(pRoot->_pLeft, blackCount, pathBlack) &&
_IsValidRBTRee(pRoot->_pRight, blackCount, pathBlack);
}
bool IsValidRBTRee()
{
Node* pRoot = GetRoot();
if (nullptr == pRoot)
return true;
if (pRoot->_color != BLACK)
{
cout << "违反性质1:根节点是黑色" << endl;
return false;
}
//获取一条路径中黑色节点数
size_t blackCount = 0;
Node* pCur = pRoot;
while (pCur)
{
if (pCur->_color == BLACK)
blackCount++;
pCur = pCur->_pLeft;
}
size_t pathBlack = 0;
return _IsValidRBTRee(pRoot, blackCount, pathBlack);
}
void InOrder()
{
_InOrder(GetRoot());
cout << endl;
}
6.源代码
#include<iostream>
using namespace std;
// 节点的颜色
enum Color { RED, BLACK };
// 红黑树节点的定义
template<class T>
struct RBTreeNode
{
RBTreeNode(const T& data = T(), Color color = RED)
: _pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _color(color)
{}
RBTreeNode<T>* _pLeft;
RBTreeNode<T>* _pRight;
RBTreeNode<T>* _pParent;
T _data;
Color _color;
};
template<class T>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
RBTree()
{
_pHead = new Node;
_pHead->_pLeft = _pHead;
_pHead->_pRight = _pHead;
}
bool Insert(const T& data)
{
Node*& pRoot = GetRoot();
//将根节点设置为黑色
if (nullptr == pRoot)
{
pRoot = new Node(data, BLACK);
pRoot->_pParent = _pHead;
return true;
}
else
{
Node* pCur = pRoot;
Node* pParent = nullptr;
//找到插入节点的位置
while (pCur)
{
pParent = pCur;
if (data < pCur->_data)
pCur = pCur->_pLeft;
else if (data > pCur->_data)
pCur = pCur->_pRight;
else
return false;
}
//插入节点
pCur = new Node(data, RED);
if (data < pParent->_data)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
pCur->_pParent = pParent;
//讨论刚才的几种情况
while (pParent != _pHead && pParent->_color == RED)
{
Node* g = pParent->_pParent;
if (pParent == g->_pLeft)
{
Node* u = g->_pRight;
//u存在且为红
if (u && RED == u->_color)
{
//情况1
pParent->_color = BLACK;
u->_color = BLACK;
g->_color = RED;
pCur = g;
pParent = pCur->_pParent;
}
else
{
//u不存在或u为黑
if (pCur == pParent->_pRight)
{
//情况3
RotateL(pParent);
swap(pCur, pParent);
}
//情况2
pParent->_color = BLACK;
g->_color = RED;
RotateR(g);
}
}
else
{
//一二三的反情况
Node* u = g->_pLeft;
if (u && RED == u->_color)
{
pParent->_color = BLACK;
u->_color = BLACK;
g->_color = RED;
pCur = g;
pParent = pCur->_pParent;
}
else
{
//u不存在或u为黑
if (pCur == pParent->_pLeft)
{
//情况3的反情况
RotateR(pParent);
swap(pCur, pParent);
}
//情况2的反情况
pParent->_color = BLACK;
g->_color = RED;
RotateL(g);
}
}
}
}
pRoot->_color = BLACK;
_pHead->_pLeft = LeftMost();
_pHead->_pRight = RightMost();
return true;
}
// 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
Node* Find(const T& data)
{
Node* pRoot = GetRoot();
if (nullptr == pRoot)return nullptr;
while (pRoot)
{
if (pRoot->_data < data)
pRoot = pRoot->_pRight;
else if (pRoot&&pRoot->_data > data)
pRoot = pRoot->_pLeft;
else
return pRoot;
}
return nullptr;
}
// 获取红黑树最左侧节点
Node* LeftMost()
{
Node* pRoot = GetRoot();
if (nullptr == pRoot)
return _pHead;
Node* pCur = pRoot;
while (pCur->_pLeft)
pCur = pCur->_pLeft;
return pCur;
}
// 获取红黑树最右侧节点
Node* RightMost()
{
Node* pRoot = GetRoot();
if (nullptr == pRoot)
return _pHead;
Node* pCur = pRoot;
while (pCur->_pRight)
pCur = pCur->_pRight;
return pCur;
}
// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
bool IsValidRBTRee()
{
Node* pRoot = GetRoot();
if (nullptr == pRoot)
return true;
if (pRoot->_color != BLACK)
{
cout << "违反性质1:根节点是黑色" << endl;
return false;
}
//获取一条路径中黑色节点数
size_t blackCount = 0;
Node* pCur = pRoot;
while (pCur)
{
if (pCur->_color == BLACK)
blackCount++;
pCur = pCur->_pLeft;
}
size_t pathBlack = 0;
return _IsValidRBTRee(pRoot, blackCount, pathBlack);
}
void InOrder()
{
_InOrder(GetRoot());
cout << endl;
}
private:
bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack)
{
if (nullptr == pRoot)
return true;
if (pRoot->_color == BLACK)
pathBlack++;
Node* pParent = pRoot->_pParent;
if (pParent != _pHead && RED == pParent->_color && RED == pRoot->_color)
{
cout << "违反性质3:不能存在连在一起的红色节点" << endl;
return false;
}
if (nullptr == pRoot->_pLeft && nullptr == pRoot->_pRight)
{
if (pathBlack != blackCount)
{
cout << "违反性质4:每条路径中黑色节点数相同" << endl;
return false;
}
}
return _IsValidRBTRee(pRoot->_pLeft, blackCount, pathBlack) &&
_IsValidRBTRee(pRoot->_pRight, blackCount, pathBlack);
}
// 左单旋
void RotateL(Node* pParent)
{
Node* pSubR = pParent->_pRight;
Node* pSubRL = pSubR->_pLeft;
pParent->_pRight = pSubRL;
if (pSubRL)
pSubRL->_pParent = pParent;
pSubR->_pLeft = pParent;
Node* pPParent = pParent->_pParent;
pSubR->_pParent = pPParent;
pParent->_pParent = pSubR;
if (pPParent == _pHead)
_pHead->_pParent = pSubR;
else
{
if (pParent == pPParent->_pLeft)
pPParent->_pLeft = pSubR;
else
pPParent->_pRight = pSubR;
}
}
void RotateR(Node* pParent)
{
Node* pSubL = pParent->_pLeft;
Node* pSubLR = pSubL->_pRight;
pParent->_pLeft = pSubLR;
if (pSubLR)
pSubLR->_pParent = pParent;
pSubL->_pRight = pParent;
Node* pPParent = pParent->_pParent;
pParent->_pParent = pSubL;
pSubL->_pParent = pPParent;
if (pPParent == _pHead)
_pHead->_pParent = pSubL;
else
{
if (pParent == pPParent->_pLeft)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
}
// 为了操作树简单起见:获取根节点
Node*& GetRoot()
{
return _pHead->_pParent;
}
void _InOrder(Node* pRoot)
{
if (pRoot == nullptr)
{
return;
}
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
}
private:
Node* _pHead;
};
6.2 测试
int main()
{
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14,8, 3, 1, 10, 6, 4, 7, 14, 13 };
RBTree<int> t1;
for (auto e : a)
{
if (e == 10)
{
int i = 0;
}
t1.Insert(e);
cout << "Insert:" << e << "->" << t1.IsValidRBTRee() << endl;
}
t1.InOrder();
cout << t1.IsValidRBTRee() << endl;
}