1. 基本概念
1.1 性质和规则:
红黑树,顾名思义即所有的节点都是通过红黑两种颜色来标识的。保证了从根到任意叶子的任意路径,最长路径(节点红黑交错)<=最短路径(节点全黑)的2倍。
- 红黑树的根节点必须是黑色的。
- 红节点的孩子必须是黑色的,没有连续的红色节点(对黑节点没有要求,父节点和孩子节点都可以是黑色的)。
- 任意节点的子路径上,黑色节点数相同。
- 所有叶子节点为黑色(NIL)。
1.1 红黑树实现原理
首先对于新增的节点必须是红节点,然后根据uncle存在情况和颜色分成两种情况进行调整:
情况一:cur为红,parent为红,grandparent为黑,uncle存在且为红,则进行向上调整。
调整规则:将parent和uncle变黑,grandparent变红。若grandparent的父亲为黑或为空(根节点),则调整结束;否则继续向上调整直到根节点,最后把根节点变黑,程序结束。
情况二:cur为红,parent为红,grandparent为黑,uncle不存在或者uncle存在且为黑。这种情况又分为cur、parent、grandparent三者在一条直线上和不在一条直线上。在一条直线上旋转一次,否则旋转两次。另外若直线向左,则右旋;直线向右,则左旋。
case1
:若cur、parent、grandparent三个节点在一条直线上。
- 若parent为grandparent的左节点,uncle不存在,则以grandparent进行右单旋,然后让parent变黑,grandparent变红:
- 若parent为grandparent的左节点,uncle存在且为黑。则以grandparent进行右单旋,然后让parent变黑,grandparent变红:
- 若parent为grandparent的右节点,则进行左单旋。调整过程和上面两种类似。
case2
:若cur、parent、grandparent三个节点在一条折线上。
- 若parent为grandparent的左节点,uncle不存在,则先以parent进行左单旋,然后以grandparent进行右单旋,让cur变黑,grandparent变红:
- 若parent为grandparent的左节点,uncle存在且为黑,则先以parent进行左单旋,然后以grandparent进行右单旋,让cur变黑,grandparent变红:
- 若parent为grandparent的右节点,则先以parent进行右单旋,然后以grandparent进行左单旋。调整过程和上面两种类似。
2. 代码实现
2.1 红黑树节点定义
enum Colour {
Red,
Black,
};
template<class K, class V>
struct RBNode {
RBNode<K, V>* _left;
RBNode<K, V>* _right;
RBNode<K, V>* _parent;
std::pair<K, V> _kv;
Colour _colour;
RBNode(const std::pair<K, V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
{}
};
2.2 红黑树插入代码
关于左旋和右旋参看平衡二叉树
template<class K, class V>
class RBTree
{
typedef RBNode<K, V> Node;
public:
RBTree()
:_root(nullptr)
{}
bool Insert(const std::pair<K, V>& kv)
{
Node* newnode = new Node(kv);
if (_root == nullptr)
{
_root = newnode;
_root->_colour = Black;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur) // 找插入的位置
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false; //找不到返回,找到了执行插入操作;等于的情况有冗余,不执行插入
}
}
cur = newnode;
cur->_colour = Red; //插入红色的节点
if (parent->_kv.first > kv.first)
{
parent->_left = newnode;
newnode->_parent = parent;
}
else //不存在等于情况,等于上面已经返回了
{
parent->_right = newnode;
newnode->_parent = parent;
}
// 调整部分
while (parent && parent->_colour == Red) //父节点存在且为红,有连续的红节点,继续向上调整
{
Node* grandparent = parent->_parent;
if (grandparent->_left == parent) // 先考虑父节点在左的情况
{
Node* uncle = grandparent->_right;
if (uncle && uncle->_colour == Red) //叔叔存在且为红,继续向上调整。
{
parent->_colour = Black;
uncle->_colour = Black;
grandparent->_colour = Red;
}
else //叔叔存在且为黑 或者 叔叔不存在
{
if (cur == parent->_left) //cur、parent、grandparent三个节点在一条直线上,右单旋
{
RotateR(grandparent);
grandparent->_colour = Red;
parent->_colour = Black;
}
// cur、parent、grandparent三个节点在折直线上,先以parent进行左单旋,然后以grandparent进行右单旋
else
{
RotateL(parent);
RotateR(grandparent);
cur->_colour = Black;
grandparent->_colour = Red;
}
break; //调整后
}
}
else //父节点在grandparent的右
{
Node* uncle = grandparent->_left;
if (uncle && uncle->_colour == Red) //叔叔存在且为红,继续向上调整。
{
parent->_colour = Black;
uncle->_colour = Black;
grandparent->_colour = Red;
}
else //叔叔存在且为黑 或者 叔叔不存在
{
if (cur == parent->_right) //cur、parent、grandparent三个节点在一条直线上,左单旋
{
RotateL(grandparent);
grandparent->_colour = Red;
parent->_colour = Black;
}
// cur、parent、grandparent三个节点在折直线上,先以parent进行右单旋,然后以grandparent进行左单旋
else
{
RotateR(parent);
RotateL(grandparent);
cur->_colour = Black;
grandparent->_colour = Red;
}
break;
}
}
_root->_colour = Black; //将根节点置黑
return true;
}
}
void RotateR(Node* parent); //左旋和右旋
void RotateL(Node* parent);
private:
Node* _root;
};
2.3 使用Graphviz打印红黑树
void PrintSingleNode(FILE* pFile, RBNode<int, int>* root)
{
if (root->_colour == Black)
{
fprintf(pFile, "\tnode[shape=record,style=\"rounded, filled\",color=black,fontcolor=white, weight = 20];\n");
}
else
{
fprintf(pFile, "\tnode[shape=record,style=\"rounded, filled\",color=red,fontcolor=white, weight = 20];\n");
}
fprintf(pFile, "\t%d[label=\"<f0> | <f1> %d | <f2> \", labelfontsize = 20];\n", root->_kv.second, root->_kv.second);
}
void WriteTree2File(FILE* pFile, RBNode<int, int>* root)
{
if (nullptr == root) return;
if (root->_parent == nullptr)
{
PrintSingleNode(pFile, root);
}
if (root->_left != nullptr)
{
PrintSingleNode(pFile, root->_left);
fprintf(pFile, "\t%d:f0:sw->%d:f1;\n", root->_kv.second, root->_left->_kv.second);
}
if (root->_right != nullptr)
{
PrintSingleNode(pFile, root->_right);
fprintf(pFile, "\t%d:f2:se->%d:f1;\n", root->_kv.second, root->_right->_kv.second);
}
WriteTree2File(pFile, root->_left);
WriteTree2File(pFile, root->_right);
}
int main()
{
RBTree<int, int> t;
int a[] = { 20,30,35,14,26, 24 ,28, 22 };
for (auto e : a)
{
t.Insert(std::make_pair(e, e));
}
FILE* fp;
fp = fopen("RBTree.dot", "w+");
fprintf(fp, "digraph G{\n");
WriteTree2File(fp, t.returenROOT());
fprintf(fp, "}");
fclose(fp);
system("dot -Tpng -O RBTree.dot");
system("RBTree.dot.png");
return 0;
}
结果: