進行紅黑樹節點的插入時候我們必須結合紅黑樹的性質,要不是真的很容易忙半天卻發現寫的代碼漏洞百出不符合紅黑樹的性質
-
主要總的來說是性質,其實籠統的來說我感覺兩條性質用來驗證就差不多了
好了,列舉額一下吧。
- 每個結點不是紅色就是黑色
- 根節點是黑色的
- 如果一個節點是紅色的,則它的兩個孩子結點是黑色的
- 對於每個結點,從該結點到其所有後代葉結點的簡單路徑上,均 包含相同數目的黑色結點
- 每個葉子結點都是黑色的(此處的葉子結點指的是空結點
因爲紅黑樹也是在搜索二叉樹的基礎上,我先畫一顆紅黑二叉樹吧。
每個節點的指向是三個方向,但是這個畫起來好累啊,構造紅黑樹的時候一看也就明白了,這裏就說明一下啊。。。
- 紅黑樹節點的定義
#include <iostream>
using namespace std;
enum COLOR{ RED, BLACK };
template<class V>
struct RBTreeNode
{
RBTreeNode(const V& value = V(), COLOR color = RED)
: _pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _value(value)
, _color(color)
{}
RBTreeNode<V>* _pLeft;
RBTreeNode<V>* _pRight;
RBTreeNode<V>* _pParent;
V _value; // 節點值域
COLOR _color; // 節點的顏色
};
這裏還是主要看一下插入吧,如果在把刪除看一下,估計要把博客寫道半夜了。
紅黑樹的插入時候主要分三種情況,三種情況都涉及到顏色調整的,兩種都涉及到旋轉,所以紅黑樹必須是要研究三代的,這裏是必須把圖畫清楚的
還有新插入節點默認着色是紅色的,如果是黑色,每條路徑上黑色節點的個數都相等這一條很重要的性質直接就違背了
我們用pCur代表當前節點的位置,用p代表父節點的位置,用g代表祖父節點,用u代表叔父節點
-
p,u都爲紅色節點,g爲黑色節點
這種情況下插入節點的P,U都爲紅色,而G節點爲黑色插入的節點對紅黑樹G的左右子樹並沒有造成什麼影響,從這種調整來說並沒有影響那條路徑上的黑色節點數量,黑色節點的數量還是相等的。 -
P 爲紅,G爲黑,U爲黑或者不存在(這裏分兩種小情況,類似AVL樹左旋右旋)
這種情況我畫的是pCur在P左,P在G左,這種情況下,以P爲旋轉中心經行左旋。如果我們的pCur在P右,P在G右,我們進行右旋,這兩種旋轉的前提都是情況2,所以就把這兩種情況當作子情況歸入2當中了
-
P爲紅,G爲黑,U爲黑或者不存在(前提條件類似情況2,但是三個節點所處的位置爲內側,而情況2爲外側)
這種情況下是左右單旋,總之了旋轉就是按我圖上所畫的,這種旋發,好像叫法還不一樣,如果位於右子樹內側的話,旋轉這是從這種旋轉的右邊角度來看道理上都是一個模子刻出來的 -
插入代碼
template<class V>
class RBTRee
{
typedef RBTreeNode<V> Node;
typedef Node* PNode;
public:
RBTRee()
{
_pHead = new Node;
_pHead->_pLeft = _pHead;
_pHead->_pRight = _pHead;
_pHead->_pParent = nullptr;
}
bool Insert(const V& value)
{
PNode& pRoot = GetRoot();
if (nullptr == pRoot)
{
pRoot = new Node(value, BLACK);
}
else
{
// 找待插入節點的位置
PNode pCur = pRoot;
PNode pParent = nullptr;
while (pCur)
{
pParent = pCur;
if (value == pCur->_value)
return true;
else if (value < pCur->_value)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
// 插入新節點
pCur = new Node(value);
if (value < pParent->_value)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
pCur->_pParent = pParent;
// 更新節點的顏色,滿足紅黑樹性質
// ....
while (pParent && RED == pParent->_color)
{
PNode grandFather = pParent->_pParent;
if (pParent == grandFather->_pLeft)
{
PNode uncle = grandFather->_pRight;
if (uncle && uncle->_color == RED)
{
// 情況一:叔叔節點存在且爲紅色
pParent->_color = BLACK;
uncle->_color = BLACK;
grandFather->_color = RED;
pCur = grandFather;
pParent = pCur->_pParent;
}
else
{
// 叔叔節點不存在 || 叔叔節點存在且爲黑色
// 情況三
if (pCur == pParent->_pRight)
{
RotateLeft(pParent);
swap(pCur, pParent);
}
// 情況二
RotateRight(grandFather);
swap(grandFather->_color, pParent->_color);
}
}
else
{
PNode unclue = grandFather->_pLeft;
// 右側--情況一
if (unclue && RED == unclue->_color)
{
unclue->_color = BLACK;
pParent->_color = BLACK;
grandFather->_color = RED;
pCur = grandFather;
pParent = pCur->_pParent;
}
else
{
// 叔叔節點不存在 || 叔叔節點存在且爲黑色
if (pCur == pParent->_pLeft)
{
RotateRight(pParent);
swap(pParent, pCur);
}
RotateLeft(grandFather);
swap(grandFather->_color, pParent->_color);
}
}
}
}
_pHead->_pLeft = LeftMost();
_pHead->_pRight = RightMost();
_pHead->_pParent->_color = BLACK;
return true;
}
插入除了分情況旋轉外,其他操作其實還是類比搜索二叉樹的。
- 最後來看一下驗證是否符合紅黑樹二叉樹性質
bool IsValidRBTree()
{
PNode& pRoot = GetRoot();
if (nullptr == pRoot)
return true;
if (RED == pRoot->_color)
{
cout << "違反性質二:根節點的顏色紅色" << endl;
return false;
}
// 獲取左左側路徑中黑色節點的個數
size_t blackCount = 0;
PNode pCur = pRoot;
while (pCur)
{
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
size_t pathCount = 0;
return _IsBalidRBTRee(pRoot, pathCount, blackCount);
}
bool _IsBalidRBTRee(PNode pRoot, size_t pathCount, size_t blackCount)
{
if (nullptr == pRoot)
return true;
PNode pParent = pRoot->_pParent;
if (pParent && RED == pParent->_color && RED == pRoot->_color)
{
cout << "違反性質三: 不能有連在一起的紅色節點" << endl;
return false;
}
if (BLACK == pRoot->_color)
pathCount++;
if (nullptr == pRoot->_pLeft && nullptr == pRoot->_pRight)
{
if (pathCount != blackCount)
{
cout << "違反性質四: 每條路徑中黑色節點必須相同" << endl;
return false;
}
}
return _IsBalidRBTRee(pRoot->_pLeft, pathCount, blackCount) &&
_IsBalidRBTRee(pRoot->_pRight, pathCount, blackCount);
}
void _InOrder(PNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_value << endl;
_InOrder(pRoot->_pRight);
}
}
PNode& GetRoot()
{
return _pHead->_pParent;
}
PNode LeftMost()
{
PNode pRoot = GetRoot();
if (nullptr == pRoot)
return _pHead;
PNode pCur = pRoot;
while (pCur->_pLeft)
pCur = pCur->_pLeft;
return pCur;
}
PNode RightMost()
{
PNode pRoot = GetRoot();
if (nullptr == pRoot)
return _pHead;
PNode pCur = pRoot;
while (pCur->_pRight)
pCur = pCur->_pRight;
return pCur;
}
void RotateLeft(PNode pParent)
{
PNode pSubR = pParent->_pRight;
PNode pSubRL = pSubR->_pLeft;
pParent->_pRight = pSubRL;
if (pSubRL)
pSubRL->_pParent = pParent;
pSubR->_pLeft = pParent;
PNode pPParent = pParent->_pParent;
pParent->_pParent = pSubR;
pSubR->_pParent = pPParent;
PNode& pRoot = GetRoot();
if (pParent == pRoot)
pRoot = pSubR;
else
{
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubR;
else
pPParent->_pRight = pSubR;
}
}
void RotateRight(PNode pParent)
{
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
pParent->_pLeft = pSubLR;
if (pSubLR)
pSubLR->_pParent = pParent;
pSubL->_pRight = pParent;
PNode pPParent = pParent->_pParent;
pSubL->_pParent = pPParent;
pParent->_pParent = pSubL;
PNode& pRoot = GetRoot();
if (pParent == pRoot)
pRoot = pSubL;
else
{
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
}
是否是紅黑二叉樹驗證時候需要驗證的性質其實主要是四條,但是最主要驗證的還是每條路徑上黑色節點的數量是否相等。驗證相等時候我們選取最左側的路徑作爲對比路徑,計算出左側節點的黑色節點個數,然後與驗證路徑對比,如果相符就滿足。
- 完整代碼
#include <iostream>
using namespace std;
enum COLOR{ RED, BLACK };
template<class V>
struct RBTreeNode
{
RBTreeNode(const V& value = V(), COLOR color = RED)
: _pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _value(value)
, _color(color)
{}
RBTreeNode<V>* _pLeft;
RBTreeNode<V>* _pRight;
RBTreeNode<V>* _pParent;
V _value; // 節點值域
COLOR _color; // 節點的顏色
};
template<class V>
class RBTRee
{
typedef RBTreeNode<V> Node;
typedef Node* PNode;
public:
RBTRee()
{
_pHead = new Node;
_pHead->_pLeft = _pHead;
_pHead->_pRight = _pHead;
_pHead->_pParent = nullptr;
}
bool Insert(const V& value)
{
PNode& pRoot = GetRoot();
if (nullptr == pRoot)
{
pRoot = new Node(value, BLACK);
}
else
{
// 找待插入節點的位置
PNode pCur = pRoot;
PNode pParent = nullptr;
while (pCur)
{
pParent = pCur;
if (value == pCur->_value)
return true;
else if (value < pCur->_value)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
// 插入新節點
pCur = new Node(value);
if (value < pParent->_value)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
pCur->_pParent = pParent;
// 更新節點的顏色,滿足紅黑樹性質
// ....
while (pParent && RED == pParent->_color)
{
PNode grandFather = pParent->_pParent;
if (pParent == grandFather->_pLeft)
{
PNode uncle = grandFather->_pRight;
if (uncle && uncle->_color == RED)
{
// 情況一:叔叔節點存在且爲紅色
pParent->_color = BLACK;
uncle->_color = BLACK;
grandFather->_color = RED;
pCur = grandFather;
pParent = pCur->_pParent;
}
else
{
// 叔叔節點不存在 || 叔叔節點存在且爲黑色
// 情況三
if (pCur == pParent->_pRight)
{
RotateLeft(pParent);
swap(pCur, pParent);
}
// 情況二
RotateRight(grandFather);
swap(grandFather->_color, pParent->_color);
}
}
else
{
PNode unclue = grandFather->_pLeft;
// 右側--情況一
if (unclue && RED == unclue->_color)
{
unclue->_color = BLACK;
pParent->_color = BLACK;
grandFather->_color = RED;
pCur = grandFather;
pParent = pCur->_pParent;
}
else
{
// 叔叔節點不存在 || 叔叔節點存在且爲黑色
if (pCur == pParent->_pLeft)
{
RotateRight(pParent);
swap(pParent, pCur);
}
RotateLeft(grandFather);
swap(grandFather->_color, pParent->_color);
}
}
}
}
_pHead->_pLeft = LeftMost();
_pHead->_pRight = RightMost();
_pHead->_pParent->_color = BLACK;
return true;
}
void InOrder()
{
_InOrder(GetRoot());
}
bool IsValidRBTree()
{
PNode& pRoot = GetRoot();
if (nullptr == pRoot)
return true;
if (RED == pRoot->_color)
{
cout << "違反性質二:根節點的顏色紅色" << endl;
return false;
}
// 獲取左左側路徑中黑色節點的個數
size_t blackCount = 0;
PNode pCur = pRoot;
while (pCur)
{
if (BLACK == pCur->_color)
blackCount++;
pCur = pCur->_pLeft;
}
size_t pathCount = 0;
return _IsBalidRBTRee(pRoot, pathCount, blackCount);
}
private:
bool _IsBalidRBTRee(PNode pRoot, size_t pathCount, size_t blackCount)
{
if (nullptr == pRoot)
return true;
PNode pParent = pRoot->_pParent;
if (pParent && RED == pParent->_color && RED == pRoot->_color)
{
cout << "違反性質三: 不能有連在一起的紅色節點" << endl;
return false;
}
if (BLACK == pRoot->_color)
pathCount++;
if (nullptr == pRoot->_pLeft && nullptr == pRoot->_pRight)
{
if (pathCount != blackCount)
{
cout << "違反性質四: 每條路徑中黑色節點必須相同" << endl;
return false;
}
}
return _IsBalidRBTRee(pRoot->_pLeft, pathCount, blackCount) &&
_IsBalidRBTRee(pRoot->_pRight, pathCount, blackCount);
}
void _InOrder(PNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_value << endl;
_InOrder(pRoot->_pRight);
}
}
PNode& GetRoot()
{
return _pHead->_pParent;
}
PNode LeftMost()
{
PNode pRoot = GetRoot();
if (nullptr == pRoot)
return _pHead;
PNode pCur = pRoot;
while (pCur->_pLeft)
pCur = pCur->_pLeft;
return pCur;
}
PNode RightMost()
{
PNode pRoot = GetRoot();
if (nullptr == pRoot)
return _pHead;
PNode pCur = pRoot;
while (pCur->_pRight)
pCur = pCur->_pRight;
return pCur;
}
void RotateLeft(PNode pParent)
{
PNode pSubR = pParent->_pRight;
PNode pSubRL = pSubR->_pLeft;
pParent->_pRight = pSubRL;
if (pSubRL)
pSubRL->_pParent = pParent;
pSubR->_pLeft = pParent;
PNode pPParent = pParent->_pParent;
pParent->_pParent = pSubR;
pSubR->_pParent = pPParent;
PNode& pRoot = GetRoot();
if (pParent == pRoot)
pRoot = pSubR;
else
{
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubR;
else
pPParent->_pRight = pSubR;
}
}
void RotateRight(PNode pParent)
{
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
pParent->_pLeft = pSubLR;
if (pSubLR)
pSubLR->_pParent = pParent;
pSubL->_pRight = pParent;
PNode pPParent = pParent->_pParent;
pSubL->_pParent = pPParent;
pParent->_pParent = pSubL;
PNode& pRoot = GetRoot();
if (pParent == pRoot)
pRoot = pSubL;
else
{
if (pPParent->_pLeft == pParent)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
}
private:
PNode _pHead;
};
void TestRBTree()
{
int array[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
RBTRee<int> t;
for (auto e : array)
t.Insert(e);
t.InOrder();
if (t.IsValidRBTree())
{
cout << "valid rbtree" << endl;
}
else
{
cout << "invalid rbtree" << endl;
}
}
int main()
{
TestRBTree();
return 0;
}
驗證一下