紅黑樹又稱二叉搜索樹,它主要是通過紅和黑兩種顏色(red、black)來標識節點。通過對任何一條從根節點到葉子節點路徑上的節點顏色進行約束,紅黑樹保證最長路徑不超過最短路徑的兩倍,所以說:紅黑樹是近似於平衡的。
■下面是紅黑樹的主要特點:
(1)紅黑樹的根節點是黑色的。
(2)紅黑樹中若一個節點是紅色的,則它的兩個子節點必須是黑色的。
(3)紅黑樹中從該節點到後代葉節點的路徑上,黑色節點數目是相同的。
◆紅黑樹的應用:
C++庫、linux內核、java庫等
◆紅黑樹與AVL樹的區別:
紅黑樹和AVL樹都是高效的平衡搜索樹,時間複雜度都是O(lgN);
紅黑樹對平衡的要求不是特別高,它只需要滿足最長路徑不超過最短路徑的兩倍,所以性能相對較高。
■下面是紅黑樹中節點結構:
enum color //枚舉節點的兩種顏色
{
RED,
BLACK,
};
template <class K, class V>
struct RBTreeNode
{
color _col;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
K _key;
V _value;
RBTreeNode(const K& key, const V& value)
:_key(key)
, _value(value)
, _left(NULL)
, _right(NULL)
, _parent(NULL)
, _col(RED) //顏色必須插入的是紅色,因爲要保證黑色節點的個數是平衡的
{ }
};
■下面分析紅黑樹的插入情況:
(1)
(2)
(3)
■下面是主要的代碼:
#pragma once
//實現紅黑樹的基本功能
/*
1.紅黑樹中的節點只能是紅色或者黑色
2.紅黑樹的根節點爲黑色
3.紅黑樹的左、右子樹的黑色節點個數是相同的
4.紅黑樹中紅色節點的兩個孩子節點必須都爲黑色節點
*/
enum color //枚舉節點的兩種顏色
{
RED,
BLACK,
};
template <class K, class V>
struct RBTreeNode
{
color _col;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
K _key;
V _value;
RBTreeNode(const K& key, const V& value)
:_key(key)
, _value(value)
, _left(NULL)
, _right(NULL)
, _parent(NULL)
, _col(RED) //顏色必須插入的是紅色,因爲要保證黑色節點的個數是平衡的
{ }
};
template <class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
RBTree()
:_root(NULL)
{}
bool Insert(const K& key, const V& value)
{
if (_root == NULL) //根節點爲空的情況,必須將根節點置爲黑色
{
_root = new Node(key, value);
_root->_col = BLACK;
return true;
}
Node* cur = _root;
Node* parent = NULL;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
break;
}
}
//尋找到數據該插入的位置
if (parent->_key < key)
{
Node* tmp = new Node(key, value);
parent->_right = tmp;
tmp->_parent = parent;
}
else if (parent->_key > key)
{
Node* tmp = new Node(key, value);
parent->_left = tmp;
tmp->_parent = parent;
}
//處理(如果父節點爲黑色,則不用對樹進行調整,樹保持紅黑樹的狀態)
while(cur != _root && parent->_col == RED)
//插入的不是根節點,且父節點的顏色爲紅
{
Node* grandFather = parent->_parent;
if (parent == grandFather->_left)
{
Node* uncle = grandFather->_right;
//情況一:需要將祖父節點置爲紅色,將父親節點和叔叔節點置爲黑色,
//下次直接從祖父節點向上進行調整
if (uncle != NULL && uncle->_col == RED)
{
grandFather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
cur = grandFather;
parent = cur->_parent;
}
else
{
//情況二:需要先進行右單旋,然後將父親節點置爲黑色,祖父節點置爲紅色
//情況三:需要先進行左單旋,然後就會轉化爲情況二
if (cur == parent->_right && parent->_right != NULL) //情況三左、右
{
_RotateL(parent);
}
parent->_col = BLACK;
grandFather->_col = RED;
_RotateR(grandFather);
}
}
else //父親節點爲祖父節點的右節點
{
Node* uncle = grandFather->_left;
//情況一:需要將祖父節點置爲紅色,將父親節點和叔叔節點置爲黑色,
//下次直接從祖父節點向上進行調整
if (uncle != NULL && uncle->_col == RED)
{
grandFather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
cur = grandFather;
parent = cur->_parent;
}
else
{
//情況二:需要先進行左單旋,然後將父親節點置爲黑色,祖父節點置爲紅色
//情況三:需要進行右單旋,然後就會轉化爲情況二
if (cur == parent->_left && parent->_left != NULL)//情況三右、左
{
_RotateR(parent);
}
parent->_col = BLACK;
grandFather->_col = RED;
_RotateL(grandFather);
}
}
}
_root->_col = BLACK;
return true;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool Check() //檢查是否爲紅黑樹
{
int countBlack = 0;
Node* cur = _root;
while (cur->_left != NULL) //統計一條路徑上的黑色節點的數目
{
if (cur->_col == BLACK)
{
countBlack++;
}
cur = cur->_left;
}
return _Check(_root, 0, countBlack);
}
protected:
bool _Check(Node* root, int blackNum, int curBalanceNum)
{
if (root == NULL)
{
return false;
}
if (root->_parent != NULL && root->_col == BLACK)
{
if (root->_parent->_col == RED)
{
return true;
}
}
if (root->_left == NULL && root->_right == NULL) //遞歸到葉子節點是的黑色節點數目是否相等
{
if (blackNum == curBalanceNum)
{
return true;
}
else
{
return false;
}
}
return _Check(root->_left, blackNum++, curBalanceNum)
&& _Check(root->_right, blackNum++, curBalanceNum);
}
void _InOrder(Node* root)
{
if (root == NULL)
{
return;
}
_InOrder(root->_left);
cout << root->_key <<":"<<root->_col<<endl;
_InOrder(root->_right);
}
void _RotateL(Node*& parent) //左單旋
{
Node* SubR = parent->_right; //新建兩個節點指針
Node* SubRL = SubR->_left;
parent->_right = SubRL; //進行調整
if (SubRL)
{
SubRL->_parent = parent;
}
SubR->_left = parent;
SubR->_parent = parent->_parent;
parent->_parent = SubR;
parent = SubR;
if (parent->_parent == NULL) //parent爲根節點的情況
{
_root = parent;
}
else //parent不爲根節點
{
Node* ppNode = parent->_parent;
if (ppNode->_key > parent->_key)
{
ppNode->_left = parent;
}
else
{
ppNode->_right = parent;
}
}
}
void _RotateR(Node*& parent) //右單旋
{
Node* SubL = parent->_left; //新建兩個節點指針
Node* SubLR = SubL->_right;
parent->_left = SubLR; //進行調整
if (SubLR)
{
SubLR->_parent = parent;
}
SubL->_right = parent;
SubL->_parent = parent->_parent;
parent->_parent = SubL;
parent = SubL;
if (parent->_parent == NULL) //parent爲根節點的情況
{
_root = parent;
}
else //parent不爲根節點
{
Node* ppNode = parent->_parent;
if (ppNode->_key > parent->_key)
{
ppNode->_left = parent;
}
else
{
ppNode->_right = parent;
}
}
}
protected:
Node* _root;
};
void Test()
{
RBTree<int, int> ht;
ht.Insert(5, 1);
ht.Insert(9, 1);
ht.Insert(3, 1);
ht.Insert(1, 1);
ht.Insert(8, 1);
ht.Insert(2, 1);
ht.Insert(4, 1);
ht.Insert(6, 1);
ht.Insert(7, 1);
ht.InOrder();
cout<<ht.Check()<<endl;
}
本文出自 “無心的執着” 博客,請務必保留此出處http://10740590.blog.51cto.com/10730590/1827889