紅黑樹(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;
}