红黑树(R-B TREE,全称:Red-Black Tree),本身是一棵二叉查找树,在其基础上附加了两个要求:
- 树中的每个结点增加了一个用于存储颜色的标志域;
- 树中没有一条路径比其他任何路径长出两倍,整棵树要接近于“平衡”的状态
这里所指的路径,指的是从任何一个结点开始,一直到其子孙的叶子结点的长度;接近于平衡:红黑树并不是平衡二叉树,只是由于对各路径的长度之差有限制,所以近似于平衡的状态。
红黑树的性质
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
红黑树节点的定义
enum Color
{
BLACK,
RED
};
template <class K, class V>
struct RBTNode
{
typedef RBTNode<K, V> Node;
Node* _pLeft = nullptr;
Node* _pRight = nullptr;
Node* _pParent = nullptr;
pair<K, V> _kv;
Color _color = RED;
};
红黑树本身作为一棵二叉查找树,所以其任务就是用于动态表中数据的插入和删除的操作。在进行该操作时,避免不了会破坏红黑树的结构,此时就需要进行适当的调整,使其重新成为一棵红黑树,可以从两个方面着手对树进行调整:
- 调整树中某些结点的指针结构;
- 调整树中某些结点的颜色;
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
1. 按照二叉搜索的树规则插入新节点
typedef RBTNode<K, V> Node;
typedef Node* pNode;
typedef Node* PNode;
RBTree()
{
_header = new Node;
_header->_pParent = nullptr;
_header->_pLeft = _header;
_header->_pRight = _header;
}
bool Insert(const pair<K, V>& kv)
{
if (_header->_pParent == nullptr)
{
pNode root = new Node;
root->_kv = kv;
root->_color = BLACK;
root->_pParent = _header;
_header->_pParent = root;
_header->_pLeft = root;
_header->_pRight = root;
return true;
}
pNode cur = _header->_pParent;
pNode parent = nullptr;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_pRight;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_pLeft;
}
else
return false;
}
pNode newNode = new Node;
newNode->_kv = kv;
if (kv.first > parent->_kv.first)
parent->_pRight = newNode;
else
parent->_pLeft = newNode;
newNode->_pParent = parent;
cur = newNode;
while (cur != _header->_pParent && cur->_pParent->_color == RED)
{
// cur, parent, gParent, uncle
pNode parent = cur->_pParent;
pNode gParent = parent->_pParent;
if (gParent->_pLeft == parent)
{
pNode uncle = gParent->_pRight;
// u存在且为红
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
gParent->_color = RED;
cur = gParent;
}
else
{
//u不存在/ u存在且为黑
//旋转,调色
//判断是否需要双旋
if (cur == parent->_pRight)
{
RotateL(parent);
swap(parent, cur);
}
//右单旋
RotateR(gParent);
parent->_color = BLACK;
gParent->_color = RED;
break;
}
}
else
{
pNode uncle = gParent->_pLeft;
//对应
if (uncle && uncle->_color == RED)
{
uncle->_color = parent->_color = BLACK;
gParent->_color = RED;
cur = gParent;
}
else
{
//判断是否要双旋
if (cur == parent->_pLeft)
{
RotateR(parent);
swap(cur, parent);
}
RotateL(gParent);
parent->_color = BLACK;
gParent->_color = RED;
break;
}
}
}
_header->_pParent->_color = BLACK;
_header->_pLeft = leftMost();
_header->_pRight = rightMost();
return true;
}
检测新节点插入后,红黑树的性质是否造到破坏
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不 需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此 时需要对红黑树分情况来讨论:
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
情况一: cur为红,p为红,g为黑,u存在且为红
情况二: cur为红,p为红,g为黑,u不存在/u为黑
情况二: cur为红,p为红,g为黑,u不存在/u为黑
// 获取红黑树中最小节点,即最左侧节点
pNode leftMost()
{
pNode cur = _header->_pParent;
while (cur && cur->_pLeft)
cur = cur->_pLeft;
return cur;
}
// 获取红黑树中最大节点,即最右侧节点
pNode rightMost()
{
pNode cur = _header->_pParent;
while (cur && cur->_pRight)
cur = cur->_pRight;
return cur;
}
void RotateL(Node* parent)
{
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
subR->_pLeft = parent;
parent->_pRight = subRL;
if (subRL)
subRL->_pParent = parent;
if (parent != _header->_pParent)
{
Node* gParent = parent->_pParent;
if (gParent->_pLeft == parent)
gParent->_pLeft = subR;
else
gParent->_pRight = subR;
subR->_pParent = gParent;
}
else
{
subR->_pParent = nullptr;
_header->_pParent = subR;
}
parent->_pParent = subR;
}
void RotateR(Node* parent)
{
Node* subL = parent->_pLeft;
Node* subLR = subL->_pRight;
//单向链接 subL , parent, subLR
subL->_pRight = parent;
parent->_pLeft = subLR;
//向上链接subLR
if (subLR)
subLR->_pParent = parent;
//subL,parent->parent 双向链接
if (parent != _header->_pParent)
{
Node* gParent = parent->_pParent;
if (gParent->_pLeft == parent)
gParent->_pLeft = subL;
else
gParent->_pRight = subL;
subL->_pParent = gParent;
}
else
{
_header->_pParent = subL;
subL->_pParent = nullptr;
}
//向上链接parent, subL
parent->_pParent = subL;
}
红黑树的验证
红黑树的检测分为两步:
1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
2. 检测其是否满足红黑树的性质
void _Inorder(pNode root)
{
if (root)
{
_Inorder(root->_pLeft);
cout << root->_kv.first << "-->" << root->_kv.second << endl;
_Inorder(root->_pRight);
}
}
void Inorder()
{
_Inorder(_header->_pParent);
}
bool IsValidRBTree()
{
PNode pRoot = _header->_pParent;
// 空树也是红黑树
if (nullptr == pRoot)
return true;
// 检测根节点是否满足情况
if (BLACK != pRoot->_color)
{
cout << "违反红黑树性质二:根节点必须为黑色" << endl;
return false;
}
// 获取任意一条路径中黑色节点的个数
size_t blackCount = 0;
PNode pCur = pRoot;
while (pCur)
{
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
size_t k = 0;
return _IsValidRBTree(pRoot, k, blackCount);
}
bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount)
{
//走到null之后,判断k和black是否相等
if (nullptr == pRoot)
{
if (k != blackCount)
{
cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
return false;
}
return true;
}
// 统计黑色节点的个数
if (BLACK == pRoot->_color)
k++;
// 检测当前节点与其双亲是否都为红色
PNode pParent = pRoot->_pParent;
if (pParent && RED == pParent->_color && RED == pRoot->_color)
{
cout << "违反性质三:没有连在一起的红色节点" << endl;
return false;
}
return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
_IsValidRBTree(pRoot->_pRight, k, blackCount);
}
代码整合
#include <iostream>
#include <time.h>
using namespace std;
enum Color
{
BLACK,
RED
};
template <class K, class V>
struct RBTNode
{
typedef RBTNode<K, V> Node;
Node* _pLeft = nullptr;
Node* _pRight = nullptr;
Node* _pParent = nullptr;
pair<K, V> _kv;
Color _color = RED;
};
template <class K, class V>
class RBTree
{
public:
typedef RBTNode<K, V> Node;
typedef Node* pNode;
typedef Node* PNode;
RBTree()
{
_header = new Node;
_header->_pParent = nullptr;
_header->_pLeft = _header;
_header->_pRight = _header;
}
bool Insert(const pair<K, V>& kv)
{
if (_header->_pParent == nullptr)
{
pNode root = new Node;
root->_kv = kv;
root->_color = BLACK;
root->_pParent = _header;
_header->_pParent = root;
_header->_pLeft = root;
_header->_pRight = root;
return true;
}
pNode cur = _header->_pParent;
pNode parent = nullptr;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_pRight;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_pLeft;
}
else
return false;
}
pNode newNode = new Node;
newNode->_kv = kv;
if (kv.first > parent->_kv.first)
parent->_pRight = newNode;
else
parent->_pLeft = newNode;
newNode->_pParent = parent;
cur = newNode;
while (cur != _header->_pParent && cur->_pParent->_color == RED)
{
// cur, parent, gParent, uncle
pNode parent = cur->_pParent;
pNode gParent = parent->_pParent;
if (gParent->_pLeft == parent)
{
pNode uncle = gParent->_pRight;
// u存在且为红
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
gParent->_color = RED;
cur = gParent;
}
else
{
//u不存在/ u存在且为黑
//旋转,调色
//判断是否需要双旋
if (cur == parent->_pRight)
{
RotateL(parent);
swap(parent, cur);
}
//右单旋
RotateR(gParent);
parent->_color = BLACK;
gParent->_color = RED;
break;
}
}
else
{
pNode uncle = gParent->_pLeft;
//对应
if (uncle && uncle->_color == RED)
{
uncle->_color = parent->_color = BLACK;
gParent->_color = RED;
cur = gParent;
}
else
{
//判断是否要双旋
if (cur == parent->_pLeft)
{
RotateR(parent);
swap(cur, parent);
}
RotateL(gParent);
parent->_color = BLACK;
gParent->_color = RED;
break;
}
}
}
_header->_pParent->_color = BLACK;
_header->_pLeft = leftMost();
_header->_pRight = rightMost();
return true;
}
// 获取红黑树中最小节点,即最左侧节点
pNode leftMost()
{
pNode cur = _header->_pParent;
while (cur && cur->_pLeft)
cur = cur->_pLeft;
return cur;
}
// 获取红黑树中最大节点,即最右侧节点
pNode rightMost()
{
pNode cur = _header->_pParent;
while (cur && cur->_pRight)
cur = cur->_pRight;
return cur;
}
void RotateL(Node* parent)
{
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
subR->_pLeft = parent;
parent->_pRight = subRL;
if (subRL)
subRL->_pParent = parent;
if (parent != _header->_pParent)
{
Node* gParent = parent->_pParent;
if (gParent->_pLeft == parent)
gParent->_pLeft = subR;
else
gParent->_pRight = subR;
subR->_pParent = gParent;
}
else
{
subR->_pParent = nullptr;
_header->_pParent = subR;
}
parent->_pParent = subR;
}
void RotateR(Node* parent)
{
Node* subL = parent->_pLeft;
Node* subLR = subL->_pRight;
//单向链接 subL , parent, subLR
subL->_pRight = parent;
parent->_pLeft = subLR;
//向上链接subLR
if (subLR)
subLR->_pParent = parent;
//subL,parent->parent 双向链接
if (parent != _header->_pParent)
{
Node* gParent = parent->_pParent;
if (gParent->_pLeft == parent)
gParent->_pLeft = subL;
else
gParent->_pRight = subL;
subL->_pParent = gParent;
}
else
{
_header->_pParent = subL;
subL->_pParent = nullptr;
}
//向上链接parent, subL
parent->_pParent = subL;
}
void _Inorder(pNode root)
{
if (root)
{
_Inorder(root->_pLeft);
cout << root->_kv.first << "-->" << root->_kv.second << endl;
_Inorder(root->_pRight);
}
}
void Inorder()
{
_Inorder(_header->_pParent);
}
bool IsValidRBTree()
{
PNode pRoot = _header->_pParent;
// 空树也是红黑树
if (nullptr == pRoot)
return true;
// 检测根节点是否满足情况
if (BLACK != pRoot->_color)
{
cout << "违反红黑树性质二:根节点必须为黑色" << endl;
return false;
}
// 获取任意一条路径中黑色节点的个数
size_t blackCount = 0;
PNode pCur = pRoot;
while (pCur)
{
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
size_t k = 0;
return _IsValidRBTree(pRoot, k, blackCount);
}
bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount)
{
//走到null之后,判断k和black是否相等
if (nullptr == pRoot)
{
if (k != blackCount)
{
cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
return false;
}
return true;
}
// 统计黑色节点的个数
if (BLACK == pRoot->_color)
k++;
// 检测当前节点与其双亲是否都为红色
PNode pParent = pRoot->_pParent;
if (pParent && RED == pParent->_color && RED == pRoot->_color)
{
cout << "违反性质三:没有连在一起的红色节点" << endl;
return false;
}
return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
_IsValidRBTree(pRoot->_pRight, k, blackCount);
}
private:
pNode _header;
};
void testRBTree()
{
RBTree<int, int> rbtree;
rbtree.Insert(make_pair(0, 0));
rbtree.Insert(make_pair(1, 0));
rbtree.Insert(make_pair(2, 0));
rbtree.Insert(make_pair(10, 0));
rbtree.Insert(make_pair(3, 0));
rbtree.Insert(make_pair(9, 0));
rbtree.Inorder();
cout << "IsValidRBTree: " << rbtree.IsValidRBTree() << endl;
}
// void testRBTree2()
// {
// srand(time(nullptr));
// int n;
// cin >> n;
// RBTree<int, int> rb;
// while (n--)
// {
// int num = rand();
// //cout << num << " ";
// rb.Insert(make_pair(num, num));
// }
// cout << endl;
// cout << "IsValidRBTree: " << rb.IsValidRBTree() << endl;
// }
int main()
{
testRBTree();
return 0;
}