紅黑樹概念
紅黑樹,是一種二叉搜索樹,但在每個結點上增加一個存儲位表示結點的顏色,可以是Red或Black。 通過對任何一條從根到葉子的路徑上各個結點着色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,是接近平衡的
紅黑樹的性質
- 每個結點不是紅色就是黑色
比特科技 - 根節點是黑色的
- 如果一個節點是紅色的,則它的兩個孩子結點是黑色的
- 對於每個結點,從該結點到其所有後代葉結點的簡單路徑上,均 包含相同數目的黑色結點
- 每個葉子結點都是黑色的(此處的葉子結點指的是空結點)
紅黑樹的實現
結構
爲了後續實現關聯式容器簡單,紅黑樹的實現中增加一個頭結點,因爲跟節點必須爲黑色,爲了與根節點進行區分,將頭結點給成黑色,並且讓頭結點的 pParent 域指向紅黑樹的根節點,pLeft域指向紅黑樹中最小的節點,_pRight域指向紅黑樹中最大的節點
定義
enum COLOR//枚舉
{
BLACK,
RED
};
template <class K, class V>
struct RBNode
{
RBNode<K, V>* _left;
RBNode<K, V>* _right;
RBNode<K, V>* _parent;
pair<K, V> _value;
//顏色
COLOR _color;
RBNode(const pair<K, V>& value = pair<K, V>())
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _value(value)
, _color(RED)
{}
};
節點的定義中,要將節點的默認顏色給成紅色的
插入
分爲兩個過程:
- 按照二叉搜索的樹規則插入新節點
- 調整更新,檢測新節點插入後,紅黑樹的性質是否造到破壞
情況一: cur爲紅,p爲紅,g爲黑,u存在且爲紅
解決方式:將p,u改爲黑,g改爲紅,然後把g當成cur,繼續向上調整
情況二: cur爲紅,p爲紅,g爲黑,u不存在/u爲黑
解決方式:p爲g的左孩子,cur爲p的左孩子,則進行右單旋轉;相反,p爲g的右孩子,cur爲p的右孩子,則進行左單旋轉,p、g變色–p變黑,g變紅
情況三: cur爲紅,p爲紅,g爲黑,u不存在/u爲黑
解決方式:p爲g的左孩子,cur爲p的右孩子,則針對p做左單旋轉;相反,p爲g的右孩子,cur爲p的左孩子,則針對p做右單旋轉,則轉換成了情況2
相較於 AVL 樹,插入比較簡單,且效率比較高,紅黑樹比AVL 樹調整次數少
上面三種情況即可總結爲:
檢驗紅黑樹
- 根節點爲黑色
- 紅色不連續
- 每條路徑黑色節點數相同
代碼
#include <utility>
#include <iostream>
using namespace std;
enum COLOR
{
BLACK,
RED
};
//節點存放<K,V>數據
template <class K, class V>
struct RBNode
{
RBNode<K, V>* _left;
RBNode<K, V>* _right;
RBNode<K, V>* _parent;
pair<K, V> _value;
//顏色
COLOR _color;
RBNode(const pair<K, V>& value = pair<K, V>())
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _value(value)
, _color(RED)
{}
};
template <class K, class V>
class RBTree
{
public:
typedef RBNode<K, V> Node;
typedef Node* pNode;
RBTree()
{
//構建空的紅黑樹
_header = new Node();
_header->_left = _header;
_header->_right = _header;
}
bool insert(const pair<K, V>& value)
{
//搜索樹的插入
if (_header->_parent == nullptr)
{
//空樹,創建根節點
pNode root = new Node(value);
root->_color = BLACK;
root->_parent = _header;
_header->_parent = root;
_header->_left = root;
_header->_right = root;
return true;
}
//從根開始搜索
pNode cur = _header->_parent;
pNode parent = nullptr;
while (cur)
{
parent = cur;
//按照key值確定位置, key不能重複
if (cur->_value.first == value.first)
return false;
else if (cur->_value.first > value.first)
cur = cur->_left;
else
cur = cur->_right;
}
cur = new Node(value);
if (parent->_value.first > cur->_value.first)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
//調整和更新(顏色): 連續的紅色時需要調整
while (cur != _header->_parent && cur->_parent->_color == RED)
{
// cur, parent, gfather, uncle
parent = cur->_parent;
pNode gfather = parent->_parent;
if (gfather->_left == parent)
{
pNode uncle = gfather->_right;
//uncle 存在且爲紅
if (uncle && uncle->_color == RED)
{
//修改顏色
parent->_color = uncle->_color = BLACK;
gfather->_color = RED;
//繼續向上更新
cur = gfather;
}
else{
//如果存在雙旋的場景,可以先進行一次單旋,使它變成單旋的場景
if (cur == parent->_right)
{
RotateL(parent);
swap(cur, parent);
}
//右旋
RotateR(gfather);
//修改顏色
parent->_color = BLACK;
gfather->_color = RED;
//停止調整
break;
}
}
else
{
pNode uncle = gfather->_left;
if (uncle && uncle->_color == RED)
{
//修改顏色
uncle->_color = parent->_color = BLACK;
gfather->_color = RED;
cur = gfather;
}
else{
//判斷是否有雙旋的場景
if (cur == parent->_left)
{
RotateR(parent);
swap(cur, parent);
}
RotateL(gfather);
parent->_color = BLACK;
gfather->_color = RED;
break;
}
}
}
//根的顏色始終是黑的
_header->_parent->_color = BLACK;
//更新 _header->_left, _header->_right
_header->_left = leftMost();
_header->_right = rightMost();
return true;
}
pNode leftMost()
{
pNode cur = _header->_parent;
while (cur && cur->_left != nullptr)
{
cur = cur->_left;
}
return cur;
}
pNode rightMost()
{
pNode cur = _header->_parent;
while (cur && cur->_right != nullptr)
{
cur = cur->_right;
}
return cur;
}
void RotateR(pNode parent)
{
pNode subL = parent->_left;
pNode subLR = subL->_right;
// 1
subL->_right = parent;
// 2
parent->_left = subLR;
// 3
if (subLR)
subLR->_parent = parent;
// 4, 5
if (parent != _header->_parent)
{
// subL <---> parent->parent
pNode gParent = parent->_parent;
if (gParent->_left == parent)
gParent->_left = subL;
else
gParent->_right = subL;
subL->_parent = gParent;
}
else
{
//更新根節點
_header->_parent = subL;
subL->_parent = nullptr;
}
// 6
parent->_parent = subL;
}
void RotateL(pNode parent)
{
pNode subR = parent->_right;
pNode subRL = subR->_left;
subR->_left = parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
if (parent != _header->_parent){
pNode gParent = parent->_parent;
if (gParent->_left == parent)
gParent->_left = subR;
else
gParent->_right = subR;
subR->_parent = gParent;
}
else
{
_header->_parent = subR;
subR->_parent = nullptr;
}
parent->_parent = subR;
}
void inOrder()
{
_inOrder(_header->_parent);
cout << endl;
}
void _inOrder(pNode root)
{
if (root)
{
_inOrder(root->_left);
cout << "< " << root->_value.first << "--->" << root->_value.second << "> ";
_inOrder(root->_right);
}
}
bool isRBTree()
{
pNode root = _header->_parent;
if (root == nullptr)
return true;
if (root->_color == RED)
{
cout << "根節點必須爲黑色" << endl;
return false;
}
//每條路徑黑色個數相同
//可以任意遍歷一條路徑
//此處爲最右路徑
pNode cur = root;
int blackCount = 0;
while (cur)
{
if (cur->_color == BLACK)
++blackCount;
cur = cur->_right;
}
int k = 0;
return _isRBTree(root, k, blackCount);
}
bool _isRBTree(pNode root, int curBlackCount, int totalBlackCount)
{
//每條路徑上黑色個數相同
//一條路徑走完
if (root == nullptr)
{
cout << "每條路徑黑色節點個數不同" << endl;
return false;
}
return true;
if (root->_color == Black)
++curBlackCount;
//沒有紅色連續
pNode parent = root->_parent;
if (parent && parent->_color == RED && root->_color == RED)
{
cout << "有紅色連續的節點" << endl;
return false;
}
return _isRBTree(root->_left, curBlackCount, totalBlackCount) && _isRBTree(root->_right, curBlackCount, totalBlackCount);
}
private:
pNode _header;
};
void testRBTree()
{
RBTree<int, int> rbt;
rbt.insert(make_pair(1, 1));
rbt.insert(make_pair(10, 1));
rbt.insert(make_pair(-1, 1));
rbt.insert(make_pair(-2, 1));
rbt.insert(make_pair(100, 1));
rbt.insert(make_pair(19, 1));
rbt.insert(make_pair(21, 1));
rbt.inOrder();
}
int main()
{
testRBTree();
return 0;
}