紅黑樹是應用的最多的二叉樹之一,包括STL中的map和set,以及Linux的內核等等都使用到了紅黑樹。今天花了一下午的時間實現了紅黑樹的一些功能,主要是插入節點的情況分析(刪除節點還在研究)。
首先紅黑樹的性質比較重要,這裏就簡單的說一下吧!
性質1. 節點是紅色或黑色。
性質2. 根節點是黑色。
性質3. 每個葉節點是黑色的。
性質4. 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
性質5. 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。
其中最重要的是最後兩條,它的特性體現在這兩條規則上,記住這兩條規則後你就能比較清楚的理解紅黑樹了。
我設計的紅黑樹的節點的形式如下:
template<class K,class V>
struct RedBlackTreeNode
{
K _key; //鍵
V _val; //值
Color _color; //顏色
RedBlackTreeNode<K, V>* _parent; //父節點
RedBlackTreeNode<K, V>* _left; //左子節點
RedBlackTreeNode<K, V>* _right; //右子節點
RedBlackTreeNode(const K& key, const V& val)
: _key(key)
, _val(val)
, _color(Red)
, _parent(NULL)
, _left(NULL)
, _right(NULL)
{}
};
下面我就分析一下紅黑樹插入節點時候的情況。
常見的情況有上述幾種,不過我這裏只畫出了一半(左半邊),右半邊和左半邊對稱,相信憑大家的能力,這點東西算不了什麼,所以我們大概只需要考慮這幾種情況就行了,在實現的時候將右半邊加上就好了,下面我就給出代碼,參考一下:
#pragma once
#include <iostream>
using namespace std;
enum Color
{
Red,
Black
};
template<class K,class V>
struct RedBlackTreeNode
{
K _key; //鍵
V _val; //值
Color _color; //顏色
RedBlackTreeNode<K, V>* _parent; //父節點
RedBlackTreeNode<K, V>* _left; //左子節點
RedBlackTreeNode<K, V>* _right; //右子節點
RedBlackTreeNode(const K& key, const V& val)
: _key(key)
, _val(val)
, _color(Red)
, _parent(NULL)
, _left(NULL)
, _right(NULL)
{}
};
template<class K,class V>
class RedBlackTree
{
typedef RedBlackTreeNode<K, V> Node;
public:
RedBlackTree()
:_root(NULL)
{}
void InOrder()
{
_InOrder(_root);
}
Node* Find(const K &key)
{
Node *cur = _root;
while (cur)
{
if (cur->_key == key)
{
return cur;
}
else if(key < cur->_key)
{
cur = cur->_left
}
else
{
cur = cur->_right;
}
}
return cur;
}
bool Insert(const K &key, const V &val)
{
if (_root == NULL)
{
_root = new Node(key, val);
_root->_color = Black;
return true;
}
//找插入點
Node *parent = NULL;
Node *cur = _root;
while (cur)
{
parent = cur;
if (cur->_key == key)
return false;
else if (cur->_key < key)
cur = cur->_right;
else
cur = cur->_left;
}
//連接節點
cur = new Node(key, val);
cur->_parent = parent;
if (parent->_key > key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
//根據紅黑樹條件調節
while (parent != _root && parent->_color == Red) //祖父節點存在且父節點爲紅色
{
Node* grandfa = parent->_parent;
if (grandfa->_left == parent)
{ //新插入節點位於grandfa的左子樹
Node *uncle = grandfa->_right;
if (uncle && uncle->_color == Red)
{
grandfa->_color = Red;
parent->_color = Black;
uncle->_color = Black;
cur = grandfa;
if (cur == _root)
{//遍歷到根了
break;
}
parent = cur->_parent;
//continue;
}
else
{
if (cur == parent->_right)
{//左單旋
_RotateL(parent);
cur = cur->_left;
parent = cur->_parent;
}
//右單旋
_RotateR(grandfa);
parent->_color = Black;
parent->_left->_color = Red;
parent->_right->_color = Red;
break;
}
}
else
{ //新插入節點位於grandfa的右子樹
Node *uncle = grandfa->_left;
if (uncle && uncle->_color == Red)
{
grandfa->_color = Red;
parent->_color = Black;
uncle->_color = Black;
cur = grandfa;
if (cur == _root)
{
break;
}
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{ //右單旋
_RotateR(parent);
cur = cur->_right;
parent = cur->_parent;
}
//左單旋
_RotateL(grandfa);
parent->_color = Black;
parent->_left->_color = Red;
parent->_right->_color = Red;
break;
}
}
}
_root->_color = Black;
return true;
}
bool Remove(const K &key)
{
Node* pos = Find(key);
if (pos == NULL)
{
return false;
}
/*
未完成
*/
return true;
}
bool IsRight()
{
//統計一條路徑上的黑色節點的個數
Node *cur = _root;
int blacknum = 0;
while (cur)
{
if (cur->_color == Black)
++blacknum;
cur = cur->_left;
}
return _CheckRBTree(_root,blacknum,0);
}
~RedBlackTree()
{}
protected:
bool _CheckRBTree(Node *root,int blacknum,int curblacknum)
{
if (root == NULL)
return true;
if (root->_color == Black)
{//黑色節點
++curblacknum;
}
else
{//紅色節點
if (root->_parent && root->_parent->_color == Red)
{//不合要求
cout << "連續兩個紅色節點" << endl;
return false;
}
}
if (root->_left == NULL && root->_right == NULL)
{//葉子結點
if (curblacknum == blacknum)
return true;
return false;
}
return _CheckRBTree(root->_left,blacknum,curblacknum)
&& _CheckRBTree(root->_right,blacknum,curblacknum);
}
void _InOrder(Node *root)
{
if (root)
{
_InOrder(root->_left);
cout << "[" << root->_key << "," << root->_val << "] ";
_InOrder(root->_right);
}
}
void _RotateL(Node *&parent)
{ //左單旋
Node *subR = parent->_right;
Node *subRL = subR->_left;
if (subRL)
{ //如果存在
subRL->_parent = parent;
}
subR->_parent = parent->_parent;
parent->_parent = subR;
subR->_left = parent;
parent->_right = subRL;
parent = subR;
if (parent->_parent)
{
Node* grandfa = parent->_parent;
if (grandfa->_key > parent->_key)
grandfa->_left = parent;
else
grandfa->_right = parent;
}
else
{
_root = parent;
}
}
void _RotateR(Node *&parent)
{ //右單旋
Node* subL = parent->_left;
Node *subLR = subL->_right;
if (subLR)
{
subLR->_parent = parent;
}
subL->_parent = parent->_parent;
parent->_parent = subL;
subL->_right = parent;
parent->_left = subLR;
parent = subL;
if (parent->_parent)
{
Node *grandfa = parent->_parent;
if (grandfa->_key > parent->_key)
grandfa->_left = parent;
else
grandfa->_right = parent;
}
else
{
_root = parent;
}
}
protected:
Node *_root;
};
void TestRedBlackTree()
{
RedBlackTree<int, int> rb;
rb.Insert(3, 1);
rb.Insert(2, 1);
rb.Insert(1, 1);
rb.Insert(5, 1);
rb.Insert(8, 1);
rb.Insert(6, 1);
rb.Insert(7, 1);
rb.InOrder();
cout <<endl<< "isRBTree ? " << rb.IsRight() << endl;
}
這裏我主要是實現的是紅黑樹的插入功能,以及中序遍歷,以及檢查紅黑樹是否正確,紅黑樹的刪除還待研究一下,下次補充上來,大家對於上面的過程有什麼問題可以給我留言,若是還有不足的地方也歡迎大家指出。