map和set的概念及使用
map和set的底層結構
map和set其底層都是按照二叉搜索樹來實現的,但是二叉搜索樹有其自身的缺陷,假如往樹中插入的元素有序或者接近有序,二叉搜索樹就會退化成單支樹,時間複雜度會退化成O(N),因此map、set等關聯式容器的底層結構是對二叉樹進行了平衡處理,即採用平衡樹來實現。
AVL樹
AVL樹的概念
二叉搜索樹雖可以縮短查找的效率,但如果數據有序或接近有序二叉搜索樹將退化爲單支樹,查找元素相當於在順序表中搜索元素,效率低下。因此,兩位俄羅斯的數學家G.M.Adelson-Velskii和E.M.Landis在1962年發明了一種解決上述問題的方法:當向二叉搜索樹中插入新結點後,如果能保證每個結點的左右子樹高度之差的絕對值不超過1(需要對樹中的結點進行調整),即可降低樹的高度,從而減少平均搜索長度。一棵AVL樹或者是空樹,或者是具有以下性質的二叉搜索樹:
- 它的左右子樹都是AVL樹
- 左右子樹高度之差(簡稱平衡因子)的絕對值不超過1(-1/0/1)
如果一棵二叉搜索樹是高度平衡的,它就是AVL樹。如果它有n個結點,其高度可保持在 O(logN),搜索時間複雜度O(logN )。
AVL樹節點的定義
template<class K, class V>
struct AVLTreeNode
{
pair<K, V> _val;
AVLTreeNode<K, V>* _pLeft;
AVLTreeNode<K, V>* _pRight;
AVLTreeNode<K, V>* _pParent;
int _bf;
AVLTreeNode(const pair<K,V>& val)
:_val(val)
, _pLeft(nullptr)//該節點的左孩子
, _pRight(nullptr)//該節點的右孩子
, _pParent(nullptr)//該節點的父節點
, _bf(0)//該節點的平衡因子
{}
};
AVL樹的插入
AVL樹就是在二叉搜索樹的基礎上引入了平衡因子,因此AVL樹也可以看成是二叉搜索樹。那麼AVL樹的插入過程可以分爲兩步:
- 按照二叉搜索樹的方式插入新節點
- 調整節點的平衡因子
bool Insert(const pair<K, V> kv)
{
// 1. 先按照二叉搜索樹的規則將節點插入到AVL樹中
// ...
// 2. 新節點插入後,AVL樹的平衡性可能會遭到破壞,此時就需要更新平衡因子,並檢測是否破壞了AVL樹的平衡性
/*
pCur插入後,pParent的平衡因子一定需要調整,在插入之前,pParent的平衡因子分爲三種情況:-1,0, 1, 分以下兩種情況:
1. 如果pCur插入到pParent的左側,只需給pParent的平衡因子-1即可
2. 如果pCur插入到pParent的右側,只需給pParent的平衡因子+1即可
此時:pParent的平衡因子可能有三種情況:0,正負1, 正負2
1. 如果pParent的平衡因子爲0,說明插入之前pParent的平衡因子爲正負1,插入後被調整成0,此時滿足AVL樹的性質,插入成功
2. 如果pParent的平衡因子爲正負1,說明插入前pParent的平衡因子一定爲0,插入後被更新成正負1,此時以pParent爲根的樹的高度增加,需要繼續向上更新
3. 如果pParent的平衡因子爲正負2,則pParent的平衡因子違反平衡樹的性質,需要對其進行旋轉處理
*/
if (_root == nullptr)
{
_root = new Node(kv);
_root->_bf = 0;
return true;
}
Node* Parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_val.first > kv.first)
{
Parent = cur;
cur = cur->_pLeft;
}
else if (cur->_val.first < kv.first)
{
Parent = cur;
cur = cur->_pRight;
}
else
{
return false;
}
}
cur = new Node(kv);
if (Parent->_val > cur->_val)
{
Parent->_pLeft = cur;
cur->_pParent = Parent;
}
else
{
Parent->_pRight = cur;
cur->_pParent = Parent;
}
while (Parent)
{
if (cur == Parent->_pLeft)
Parent->_bf--;
else
{
Parent->_bf++;
}
if (Parent->_bf == 0)
break;
else if (abs(Parent->_bf) == 1)
{
cur = Parent;
Parent = Parent->_pParent;
}
else if (abs(Parent->_bf) == 2)
{
if (Parent->_bf == 2){
if (cur->_bf == 1){
RotateL(Parent);
}
else if (cur->_bf == -1){
RotateRL(Parent);
}
}
else if (Parent->_bf == -2){
if (cur->_bf == -1){
RotateR(Parent);
}
else if (cur->_bf == 1){
RotateLR(Parent);
}
}
break;
}
else
{
assert(false);
}
}
return true;
}
AVL樹的旋轉
如果在一棵原本是平衡的AVL樹中插入一個新節點,可能造成不平衡,此時必須調整樹的結構,使之平衡化。根據節點插入位置的不同,AVL樹的旋轉分爲四種:
1. 新節點插入較高左子樹的左側—左左:右單旋
/*
上圖在插入前,AVL樹是平衡的,新節點插入到30的左子樹(注意:此處不是左孩子)中,30左子樹增
加
了一層,導致以60爲根的二叉樹不平衡,要讓60平衡,只能將60左子樹的高度減少一層,右子樹增加
一層,
即將左子樹往上提,這樣60轉下來,因爲60比30大,只能將其放在30的右子樹,而如果30有右子
樹,右子樹根的值一定大於30,小於60,只能將其放在60的左子樹,旋轉完成後,更新節點的平衡因子
即可。在旋轉過程中,有以下幾種情況需要考慮:
1. 30節點的右孩子可能存在,也可能不存在
2. 60可能是根節點,也可能是子樹
如果是根節點,旋轉完成後,要更新根節點
如果是子樹,可能是某個節點的左子樹,也可能是右子樹
同學們再此處可舉一些詳細的例子進行畫圖,考慮各種情況,加深旋轉的理解
*/
void _RotateR(PNode pParent)
{
// pSubL: pParent的左孩子
// pSubLR: pParent左孩子的右孩子,注意:該
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
// 旋轉完成之後,30的右孩子作爲雙親的左孩子
pParent->_pLeft = pSubLR;
// 如果30的左孩子的右孩子存在,更新親雙親
if(pSubLR)
pSubLR->_pParent = pParent;
// 60 作爲 30的右孩子
pSubL->_pRight = pParent;
// 因爲60可能是棵子樹,因此在更新其雙親前必須先保存60的雙親
PNode pPParent = pParent->_pParent;
// 更新60的雙親
pParent->_pParent = pSubL;
// 更新30的雙親
pSubL->_pParent = pPParent;
// 如果60是根節點,根新指向根節點的指針
if(NULL == pPParent)
{
_pRoot = pSubL;
pSubL->_pParent = NULL;
}
else
{
// 如果60是子樹,可能是其雙親的左子樹,也可能是右子樹
if(pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
// 根據調整後的結構更新部分節點的平衡因子
pParent->_bf = pSubL->_bf = 0;
}
2. 新節點插入較高右子樹的右側—右右:左單旋
//左單旋
void RotateL(Node* parent)
{
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
parent->_pRight = subRL;
if (subRL)
subRL->_pParent = parent;
subR->_pLeft = parent;
Node* ppNode = parent->_pParent;
parent->_pParent = subR;
if (parent == _root)
{
_root = subR;
_root->_pParent = nullptr;
}
else
{
if (ppNode->_pLeft == subR)
ppNode->_pLeft = subR;
else
{
ppNode->_pRight = subR;
}
subR->_pParent = ppNode;
}
parent->_bf = subR->_bf = 0;
}
3. 新節點插入較高左子樹的右側—左右:先左單旋再右單旋
將雙旋變成單旋後再旋轉,即:先對30進行左單旋,然後再對90進行右單旋,旋轉完成後再考慮平衡因子的更新。
// 旋轉之前,60的平衡因子可能是-1/0/1,旋轉完成之後,根據情況對其他節點的平衡因子進行調整
void _RotateLR(PNode pParent)
{
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
// 旋轉之前,保存pSubLR的平衡因子,旋轉完成之後,需要根據該平衡因子來調整其他節點的平
衡因子
int bf = pSubLR->_bf;
// 先對30進行左單旋
_RotateL(pParent->_pLeft);
// 再對90進行右單旋
_RotateR(pParent);
if(1 == bf)
pSubL->_bf = -1;
else if(-1 == bf)
pParent->_bf = 1;
}
4. 新節點插入較高右子樹的左側—右左:先右單旋再左單旋
void RotateRL(Node* parent)
{
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
int bf = subRL->_bf; //保存subRL的平衡因子,因爲在下面兩行會置 0
RotateR(parent->_pRight);
RotateL(parent);
if (bf == 0){
parent->_bf = subRL->_bf = subR->_bf = 0;
}
else if (bf == 1){ //在c插入的新結點
subR->_bf = 0;
parent->_bf = -1;
subRL->_bf = 0;
}
else if (bf == -1){ //在b插入的新結點
parent->_bf = 0;
subR->_bf = 1;
subRL->_bf = 0;
}
}
總結:
假如以pParent爲根的子樹不平衡,即pParent的平衡因子爲2或者-2,分以下情況考慮
- pParent的平衡因子爲2,說明pParent的右子樹高,設pParent的右子樹的根爲pSubR
- 當pSubR的平衡因子爲1時,執行左單旋
- 當pSubR的平衡因子爲-1時,執行右左雙旋
- pParent的平衡因子爲-2,說明pParent的左子樹高,設pParent的左子樹的根爲pSubL
- 當pSubL的平衡因子爲-1是,執行右單旋
- 當pSubL的平衡因子爲1時,執行左右雙旋
旋轉完成後,原pParent爲根的子樹個高度降低,已經平衡,不需要再向上更新。
AVL樹的驗證
AVL樹是在二叉搜索樹的基礎上加入了平衡性的限制,因此要驗證AVL樹,可以分兩步:
- 驗證其爲二叉搜索樹
如果中序遍歷可得到一個有序的序列,就說明爲二叉搜索樹 - 驗證其爲平衡樹
每個節點子樹高度差的絕對值不超過1(注意節點中如果沒有平衡因子)節點的平衡因子是否計算正確
int _Height(PNode pRoot);
bool _IsBalanceTree(PNode pRoot)
{
// 空樹也是AVL樹
if (nullptr == pRoot) return true;
// 計算pRoot節點的平衡因子:即pRoot左右子樹的高度差
int leftHeight = _Height(pRoot->_pLeft);
int rightHeight = _Height(pRoot->_pRight);
int diff = rightHeight - leftHeight;
// 如果計算出的平衡因子與pRoot的平衡因子不相等,或者
// pRoot平衡因子的絕對值超過1,則一定不是AVL樹
if (diff != pRoot->_bf || (diff > 1 || diff < -1))
return false;
// pRoot的左和右如果都是AVL樹,則該樹一定是AVL樹
return _IsBalanceTree(pRoot->_pLeft) && _IsBalanceTree(pRoot->_pRight);
}
AVL樹的刪除
因爲AVL樹也是二叉搜索樹,可按照二叉搜索樹的方式將節點刪除,然後再更新平衡因子,只不錯與刪除不同的時,刪除節點後的平衡因子更新,最差情況下一直要調整到根節點的位置。
AVL樹的性能
AVL樹是一棵絕對平衡的二叉搜索樹,其要求每個節點的左右子樹高度差的絕對值都不超過1,這樣可以保證查詢時高效的時間複雜度,即 。但是如果要對AVL樹做一些結構修改的操作,性能非常低下,比如:
- 插入時要維護其絕對平衡,旋轉的次數比較多,更差的是在刪除時,有可能一直要讓旋轉持續到根的位置。
因此:如果需要一種查詢高效且有序的數據結構,而且數據的個數爲靜態的(即不會改變),可以考慮AVL樹,但一個結構經常修改,就不太適合。
紅黑樹
紅黑樹的概念
紅黑樹,是一種二叉搜索樹,但在每個結點上增加一個存儲位表示結點的顏色,可以是Red或Black。 通過對任何一條從根到葉子的路徑上各個結點着色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,因而是接近平衡的。
紅黑樹的性質
- 每個結點不是紅色就是黑色
- 根節點是黑色的
- 如果一個節點是紅色的,則它的兩個孩子結點是黑色的
- 對於每個結點,從該結點到其所有後代葉結點的簡單路徑上,均 包含相同數目的黑色結點
- 每個葉子結點都是黑色的(此處的葉子結點指的是空結點)
紅黑樹節點的定義
// 節點的顏色
enum Color{RED, BLACK};
// 紅黑樹節點的定義
template<class ValueType>
struct RBTreeNode
{
RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _color(color)
{}
RBTreeNode<ValueType>* _pLeft; // 節點的左孩子
RBTreeNode<ValueType>* _pRight; // 節點的右孩子
RBTreeNode<ValueType>* _pParent; // 節點的雙親(紅黑樹需要旋轉,爲了實現簡單給出該字段)
ValueType _data; // 節點的值域
Color _color; // 節點的顏色
};
紅黑樹結構
爲了後續實現關聯式容器簡單,紅黑樹的實現中增加一個頭結點,因爲跟節點必須爲黑色,爲了與根節點進行區分,將頭結點給成黑色,並且讓頭結點的 pParent 域指向紅黑樹的根節點,pLeft域指向紅黑樹中最小的節點,_pRight域指向紅黑樹中最大的節點,如下:
紅黑樹的插入操作
紅黑樹是在二叉搜索樹的基礎上加上其平衡限制條件,因此紅黑樹的插入可分爲兩步:
1. 按照二叉搜索的樹規則插入新節點
template<class ValueType>
class RBTree{
typedef RBTreeNode Node;
public:
bool Insert(const ValueType& data)
{
……
PNode& pRoot = GetRoot();
if (nullptr == pRoot)
{
pRoot = new Node(data, BLACK);
// 根的雙親爲頭節點
pRoot->_pParent = _pHead;
_pHead->_pParent = pRoot;
}
else
{
// 1. 按照二叉搜索的樹方式插入新節點
// 2. 檢測新節點插入後,紅黑樹的性質是否造到破壞,
// 若滿足直接退出,否則對紅黑樹進行旋轉着色處理
}
// 根節點的顏色可能被修改,將其改回黑色
pRoot->_color = BLACK;
_pHead->_pLeft = LeftMost();
_pHead->_pRight = RightMost();
return true;
}
private:
PNode& GetRoot(){ return _pHead->_pParent; }
// 獲取紅黑樹中最小節點,即最左側節點
PNode LeftMost();
// 獲取紅黑樹中最大節點,即最右側節點
PNode RightMost();
private:
Node* _root;
};
2. 檢測新節點插入後,紅黑樹的性質是否造到破壞
因爲新節點的默認顏色是紅色,因此:如果其雙親節點的顏色是黑色,沒有違反紅黑樹任何性質,則不需要調整;但當新插入節點的雙親節點顏色爲紅色時,就違反了性質三不能有連在一起的紅色節點,此時需要對紅黑樹分情況來討論:
- 約定:cur爲當前節點,p爲父節點,g爲祖父節點,u爲叔叔節點
- 情況一: 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存在且爲紅
- 情況三: cur爲紅,p爲紅,g爲黑,u不存在/u爲黑
p爲g的左孩子,cur爲p的右孩子,則針對p做左單旋轉;相反,
p爲g的右孩子,cur爲p的左孩子,則針對p做右單旋轉
則轉換成了情況2
pair<iterator, bool> Insert(const T& val){
//插入節點
if (_root == nullptr)
{
_root = new Node(val);
_root->_color = BLACK;
return make_pair(iterator(_root), true);
}
//尋找位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_data < val){
parent = cur;
cur = cur->_pRight;
}
else if (cur->_data > val){
parent = cur;
cur = cur->_pLeft;
}
else{
return make_pair(iterator(cur), false);
}
}
cur = new Node(val);
Node* newnode = cur;
cur->_color = RED;
//插入適當位置
if (parent->_data < val){
parent->_pRight = cur;
cur->_pParent = parent;
}
else{
parent->_pLeft = cur;
cur->_pParent = parent;
}
//變色處理
//父節點爲紅色節點
while (parent && parent->_color == RED){
Node* grandfather = parent->_pParent;
//父親是祖父節點左孩子
if (parent == grandfather->_pLeft){
Node* uncle = grandfather->_pRight;
// 1.叔叔節點存在且爲紅
if (uncle && uncle->_color == RED){
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_pParent;
}
//叔叔節點不存在或者叔叔節點爲黑
else{
if (cur == parent->_pRight){
RotateL(parent);
swap(parent, cur);
}
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
break;
}
}
//父親節點是祖父節點右孩子
else{
Node* uncle = grandfather->_pLeft;
if (uncle && uncle->_color == RED){
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_pParent;
}
else{
if (cur == parent->_pLeft){
RotateR(parent);
swap(parent, cur);
}
RotateL(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
break;
}
}
}
_root->_color = BLACK;
return make_pair(iterator(newnode), true);
}
void RotateL(Node* parent){
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
parent->_pRight = subRL;
if (subRL)
subRL->_pParent = parent;
subR->_pLeft = parent;
Node* pNode = parent->_pParent;
parent->_pParent = subR;
if (parent == _root){
_root = subR;
_root->_pParent = nullptr;
}
else{
if (pNode->_pLeft == parent)
pNode->_pLeft = subR;
else
pNode->_pRight = subR;
subR->_pParent = pNode;
}
}
void RotateR(Node* parent){
Node* subL = parent->_pLeft;
Node* subLR = subL->_pRight;
parent->_pLeft = subLR;
if (subLR)
subLR->_pParent = parent;
subL->_pRight = parent;
Node* pNode = parent->_pParent;
parent->_pParent = subL;
if (pNode == nullptr){
_root = subL;
_root->_pParent = nullptr;
}
else{
if (pNode->_pLeft == parent){
pNode->_pLeft = subL;
}
else{
pNode->_pRight = subL;
}
subL->_pParent = pNode;
}
}
紅黑樹的驗證
紅黑樹的檢測分爲兩步:
- 檢測其是否滿足二叉搜索樹(中序遍歷是否爲有序序列)
- 檢測其是否滿足紅黑樹的性質
bool IsValidRBTree(){
Node* pRoot = _root;
// 空樹也是紅黑樹
if (nullptr == pRoot)
return true;
// 檢測根節點是否滿足情況
if (BLACK != pRoot->_color){
cout << "違反紅黑樹性質二:根節點必須爲黑色" << endl;
return false;
}
// 獲取任意一條路徑中黑色節點的個數
size_t blackCount = 0;
Node* pCur = pRoot;
while (pCur){
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
// 檢測是否滿足紅黑樹的性質,k用來記錄路徑中黑色節點的個數
size_t k = 0;
return _IsValidRBTree(pRoot, k, blackCount);
}
bool _IsValidRBTree(Node* 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++;
// 檢測當前節點與其雙親是否都爲紅色
Node* 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);
}
紅黑樹與AVL樹的比較
紅黑樹和AVL樹都是高效的平衡二叉樹,增刪改查的時間複雜度都是O(log N),紅黑樹不追求絕對平衡,其只需保證最長路徑不超過最短路徑的2倍,相對而言,降低了插入和旋轉的次數,所以在經常進行增刪的結構中性能比AVL樹更優,而且紅黑樹實現比較簡單,所以實際運用中紅黑樹更多。
紅黑樹的迭代器
迭代器的好處是可以方便遍歷,是數據結構的底層實現與用戶透明。如果想要給紅黑樹增加迭代器,需要考慮以前問題:
- begin()與end()
STL明確規定,begin()與end()代表的是一段前閉後開的區間,而對紅黑樹進行中序遍歷後,可以得到一個有序的序列,因此:begin()可以放在紅黑樹中最小節點(即最左側節點)的位置,end()放在最大節點
(最右側節點)的下一個位置,關鍵是最大節點的下一個位置在哪塊?能否給成nullptr呢?答案是行不通的,因爲對end()位置的迭代器進行–操作,必須要能找最後一個元素,此處就不行,因此最好的方式是將end()放在頭結點的位置:
// 找迭代器的下一個節點,下一個節點肯定比其大
void Increasement()
{
//分兩種情況討論:_pNode的右子樹存在和不存在
// 右子樹存在
if(_pNode->_pRight)
{
// 右子樹中最小的節點,即右子樹中最左側節點
_pNode = _pNode->_pRight;
while(_pNode->_pLeft)
_pNode = _pNode->_pLeft;
}
else
{
// 右子樹不存在,向上查找,直到_pNode != pParent->right
PNode pParent = _pNode->_pParent;
while(pParent->_pRight == _pNode)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
// 特殊情況:根節點沒有右子樹
if(_pNode->_pRight != pParent)
_pNode = pParent;
}
}
// 獲取迭代器指向節點的前一個節點
void Decreasement()
{
//分三種情況討論:_pNode 在head的位置,_pNode 左子樹存在,_pNode 左子樹不存在
// 1. _pNode 在head的位置,--應該將_pNode放在紅黑樹中最大節點的位置
if(_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)
_pNode = _pNode->_pRight;
else if(_pNode->_pLeft)
{
// 2. _pNode的左子樹存在,在左子樹中找最大的節點,即左子樹中最右側節點
_pNode = _pNode->_pLeft;
while(_pNode->_pRight)
_pNode = _pNode->_pRight;
}
else
{
// _pNode的左子樹不存在,只能向上找
PNode pParent = _pNode->_pParent;
while(_pNode == pParent->_pLeft)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
_pNode = pParent;
}
}
RBTree的完整代碼
#include<iostream>
using namespace std;
enum Color{
RED,
BLACK
};
template<class ValueType>
struct RBTreeNode
{
RBTreeNode<ValueType>* _pLeft;
RBTreeNode<ValueType>* _pRight;
RBTreeNode<ValueType>* _pParent;
ValueType _data;
Color _color;
RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
:_pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _color(color)
{}
};
template<class T,class Ptr,class Ref>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T, Ptr, Ref> Self;
Node* _node;
RBTreeIterator(Node* node)
:_node(node)
{}
Ref operator*(){
return _node->_data;
}
Ref operator->(){
return &_node->_data;
}
Self& operator++()
{
if (_node->_pRight != nullptr)
{
_node = _node->_pRight;
while (_node->_pLeft != nullptr)
{
_node = _node->_pLeft;
}
}
else
{
Node* parent = _node->_pParent;
while (parent != nullptr && _node == parent->_pRight){
_node = parent;
parent = _node->_parent;
}
_node = parent;
}
return *this;
}
Self operator++(int){
Self tmp(*this);
++(*this);
return tmp;
}
Self& operator--(){
if (_node->_pLeft != nullptr)
{
_node = _node->_pLeft;
while (_node->_pLeft != nullptr)
{
_node = _node->_pRight;
}
}
else
{
Node* parent = _node->_pParent;
while (parent != nullptr && _node == parent->_pLeft){
_node = parent;
parent = _node->_parent;
}
_node = parent;
}
return *this;
}
Self operator--(int){
Self tmp(*this);
--(*this);
return tmp;
}
bool operator != (const Self& s) const{
return _node != s._node;
}
bool operator == (const Self& s) const{
return _node == s._node;
}
};
template<class K, class T>
class RBTree{
typedef RBTreeNode<T> Node;
public:
typedef RBTreeIterator<T, T*, T&> iterator;
typedef RBTreeIterator<T, const T*, const T&> const_iterator;
RBTree() = default;
// 拷貝構造 + operator=
// 析構函數
iterator begin(){
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
iterator end(){
return iterator(nullptr);
}
const_iterator begin() const{
Node* cur = _root;
while (cur && cur->_left){
cur = cur->_left;
}
return const_iterator(cur);
}
const_iterator end() const{
return const_iterator(nullptr);
}
pair<iterator, bool> Insert(const T& val){
//插入節點
if (_root == nullptr)
{
_root = new Node(val);
_root->_color = BLACK;
return make_pair(iterator(_root), true);
}
//尋找位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_data < val){
parent = cur;
cur = cur->_pRight;
}
else if (cur->_data > val){
parent = cur;
cur = cur->_pLeft;
}
else{
return make_pair(iterator(cur), false);
}
}
cur = new Node(val);
Node* newnode = cur;
cur->_color = RED;
//插入適當位置
if (parent->_data < val){
parent->_pRight = cur;
cur->_pParent = parent;
}
else{
parent->_pLeft = cur;
cur->_pParent = parent;
}
//變色處理
//父節點爲紅色節點
while (parent && parent->_color == RED){
Node* grandfather = parent->_pParent;
//父親是祖父節點左孩子
if (parent == grandfather->_pLeft){
Node* uncle = grandfather->_pRight;
// 1.叔叔節點存在且爲紅
if (uncle && uncle->_color == RED){
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_pParent;
}
//叔叔節點不存在或者叔叔節點爲黑
else{
if (cur == parent->_pRight){
RotateL(parent);
swap(parent, cur);
}
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
break;
}
}
//父親節點是祖父節點右孩子
else{
Node* uncle = grandfather->_pLeft;
if (uncle && uncle->_color == RED){
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_pParent;
}
else{
if (cur == parent->_pLeft){
RotateR(parent);
swap(parent, cur);
}
RotateL(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
break;
}
}
}
_root->_color = BLACK;
return make_pair(iterator(newnode), true);
}
void RotateL(Node* parent){
Node* subR = parent->_pRight;
Node* subRL = subR->_pLeft;
parent->_pRight = subRL;
if (subRL)
subRL->_pParent = parent;
subR->_pLeft = parent;
Node* pNode = parent->_pParent;
parent->_pParent = subR;
if (parent == _root){
_root = subR;
_root->_pParent = nullptr;
}
else{
if (pNode->_pLeft == parent)
pNode->_pLeft = subR;
else
pNode->_pRight = subR;
subR->_pParent = pNode;
}
}
void RotateR(Node* parent){
Node* subL = parent->_pLeft;
Node* subLR = subL->_pRight;
parent->_pLeft = subLR;
if (subLR)
subLR->_pParent = parent;
subL->_pRight = parent;
Node* pNode = parent->_pParent;
parent->_pParent = subL;
if (pNode == nullptr){
_root = subL;
_root->_pParent = nullptr;
}
else{
if (pNode->_pLeft == parent){
pNode->_pLeft = subL;
}
else{
pNode->_pRight = subL;
}
subL->_pParent = pNode;
}
}
iterator Find(const K& k){
Node* cur = _root;
while (cur){
KeyOfValue kov;
if (kov(cur->_data) < k){
cur = cur->_pRight;
}
else if (kov(cur->_data) > k){
cur = cur->_pLeft;
}
else{
return iterator(cur);
}
}
return end();
}
bool IsValidRBTree(){
Node* pRoot = _root;
// 空樹也是紅黑樹
if (nullptr == pRoot)
return true;
// 檢測根節點是否滿足情況
if (BLACK != pRoot->_color){
cout << "違反紅黑樹性質二:根節點必須爲黑色" << endl;
return false;
}
// 獲取任意一條路徑中黑色節點的個數
size_t blackCount = 0;
Node* pCur = pRoot;
while (pCur){
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
// 檢測是否滿足紅黑樹的性質,k用來記錄路徑中黑色節點的個數
size_t k = 0;
return _IsValidRBTree(pRoot, k, blackCount);
}
bool _IsValidRBTree(Node* 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++;
// 檢測當前節點與其雙親是否都爲紅色
Node* 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);
}
void InOrder(){
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root){
if (root == nullptr)
return;
_InOrder(root->_pLeft);
cout << root->_data << " ";
_InOrder(root->_pRight);
}
private:
Node* _root = nullptr;
};
void TestRBtree(){
RBTree<int, int> t;
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, };
for (auto e : a){
t.Insert(e);
}
t.InOrder();
cout << t.IsValidRBTree() << endl;
}
map的模擬實現
#include"RBTree.h"
namespace fxl
{
template<class K, class V>
class map
{
typedef pair<K, V> ValueType;
// 作用:將value中的key提取出來
struct KeyOfValue
{
const K& operator()(const ValueType& v)
{
return v.first;
}
};
typedef RBTree<K, ValueType, KeyOfValue> RBTree;
public:
typedef typename RBTree::Iterator iterator;
public:
map(){}
iterator begin(){ return _t.Begin(); }
iterator end(){ return _t.End(); }
size_t size()const{ return _t.Size(); }
bool empty()const{ return _t.Empty(); }
V& operator[](const K& key)
{
return (*(_t.Insert(ValueType(key, V()))).first).second;
}
const V& operator[](const K& key)const;
pair<iterator, bool> insert(const ValueType& data) { return _t.Insert(data); }
void clear(){ _t.Clear(); }
iterator find(const K& key){ return _t.Find(key); }
private:
RBTree _t;
};
}
set的模擬實現
#include"RBTree.h"
namespace fxl
{
template<class K>
class set
{
typedef K ValueType;
struct KeyOfValue
{
const K& operator()(const ValueType& key)
{
return key;
}
};
// 紅黑樹類型重命名
typedef RBTree<K, ValueType, KeyOfValue> RBTree;
public:
typedef typename RBTree::Iterator iterator;
public:
Set(){}
iterator begin(){ return _t.Begin(); }
iterator end(){ return _t.End(); }
size_t size()const{ return _t.Size(); }
bool empty()const{ return _t.Empty(); }
pair<iterator, bool> insert(const ValueType& data)
{
return _t.Insert(data);
}
void clear(){ _t.Clear(); }
iterator find(const K& key){ return _t.Find(key); }
private:
RBTree _t;
};
}