其实很多人会意识到,如果使用二叉搜索树,在数据不好的情况下,最后查找的效果会变成线性。例如数据 1 2 3 4 5 6 7,根为1,其他数据依次插入在右侧,最后变成一个线性结构,性能就会变差。如何改善二叉搜索树,使得树的高度接近logN,这种特殊的二叉搜索树就可以用红黑树来实现。
红黑树满足的五个条件
1. 每个节点或是红色,或者是黑色
2. 根是黑色的
3. 每个叶节点(NIL)是黑色的
4. 如果一个节点是红色的,则它的两个儿子都是黑色的
5. 对于每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点。
4 5 性质保证了 这个特殊的二叉搜索树局部上是趋近平衡的,算法导论中证明了一棵具有n个内点的红黑树,高度至多是2lg(n+1),图是一个rb树的例子
树节点的定义为:
typedef enum{RED,BLACK} Color;
template<typename T>
class RBTreeNode
{
public:
RBTreeNode()
{
pLeft = pRight = pParent = NULL;
}
RBTreeNode(T _data):data(_data)
{
pLeft = pRight = pParent = NULL;
}
public:
T data;
RBTreeNode<T> *pLeft;
RBTreeNode<T> *pRight;
RBTreeNode<T> *pParent;
Color color;
};
除了多一个数据颜色以外,其他的定义和二叉搜索树是一样的。
操作:
正因为红黑树要求满足这五个性质,所以在构造插入节点的,和删除节点的过程中,都要动态局部修改树,使得性质得到继续的满足。
在插入的过程中,经常会遇到用左旋和右旋操作来改变树的结构,同时维持二叉搜索树的性质。
用一个图来说明左旋和右旋,图中是左旋,右旋是左旋的一个相反的过程。
其实要处理的包括三对指针域
例如旋转的是pNode和它的右孩子
则要修改pNode->Parent 的连接关系,pNode的父子关系,还有pNode右孩子的左孩子(14为根的那个子树)
相应的代码如下:
//left rotate
//
template<typename T>
void RBTree<T>::left_rotate(RBTreeNode<T> *_pNode)
{
RBTreeNode<T> *pRightSon = _pNode->pRight;
if(pRightSon == &nil)
{
return ;
}
//process the parent of _pNode
if(_pNode->pParent != &nil)
{
if(_pNode->pParent->pLeft == _pNode)
{
_pNode->pParent->pLeft = pRightSon;
pRightSon->pParent = _pNode->pParent;
}
else if(_pNode->pParent->pRight == _pNode)
{
_pNode->pParent->pRight = pRightSon;
pRightSon->pParent = _pNode->pParent;
}
}
//in this situation, parent of _pNode is the root
else
{
m_pRoot = pRightSon;
pRightSon->pParent = &nil;
}
RBTreeNode<T> *pTmp = _pNode->pLeft;
pRightSon->pLeft = _pNode;
_pNode->pParent = pRightSon;
_pNode->pRight = pTmp;
pTmp->pParent = _pNode;
}
不难写出右旋代码。
在插入一个新节点的时候,和普通二叉搜索树一样,找到一个合适的位置插入。并且使得新的节点是红色,代码如下:
//insert a new node into the rb tree
template<typename T>
void RBTree<T>::insert(T data)
{
//create a new node
RBTreeNode<T>* newNode = new RBTreeNode<T>(data);
newNode->color = RED;
newNode->pParent = newNode->pRight = newNode->pLeft = &nil;
//if current tree is empty
if(m_pRoot == NULL)
{
m_pRoot = newNode;
m_pRoot -> color = BLACK;
m_pRoot->pParent = &nil;
return ;
}
//pNode for iterating
//pInsert for insertions
RBTreeNode<T> *pNode = m_pRoot;
RBTreeNode<T> *pInsert = &nil;
while(pNode != &nil)
{
if(data > pNode->data)
{
pInsert = pNode;
pNode = pNode->pRight;
}
else if(data < pNode->data)
{
pInsert = pNode;
pNode = pNode->pLeft;
}
else
{
return ;
}
}
if(pInsert != &nil)
{
if(data > pInsert->data)
{
pInsert->pRight = newNode;
}
else
{
pInsert->pLeft = newNode;
}
newNode->pParent = pInsert;
insert_fix_up(newNode);
}
}
但是这样一来,红黑树的性质就可能被破坏掉了。如果树种仅有一个节点,性质2被破坏,如果树中有多个节点,新插入的newNode的父亲如果是红色节点,这个时候性质4就被破坏了。实际中我们主要关注性质4被破坏,因为性质2 的破坏很容易修正,仅仅将根设为黑色即可,因为这样的操作不会破坏原有的任何性质。
如果newNode是红色,并且父亲是黑色的。很显然,性质不会破坏
只要父亲是红色的,则性质就不对。所以我们分析这个如何处理。
首先看newNode的叔叔节点,假如叔叔是红色的,这样的话,我们只要同时变化newNode的父亲和叔叔的颜色为黑色,同时设置爷爷为红色,则所有性质都可以保持。
但是如果叔叔是黑色的,则不能仅仅这样设置了,以为无论如何简单的改变颜色都会使得性质破坏掉。
所以这个时候正确的操作是先旋转在旋转。
代码如下:
template <typename T>
void RBTree<T>::insert_fix_up(RBTreeNode<T> *_pNode)
{
RBTreeNode<T>* y;
while(_pNode->pParent->color == RED)
{
if(_pNode->pParent == _pNode->pParent->pParent->pLeft)
{
y = _pNode->pParent;
if(y->pParent->pRight->color == RED)
{
_pNode->pParent->pParent->pRight->color = BLACK;
_pNode->pParent->color = BLACK;
_pNode->pParent->pParent->color = RED;
_pNode = _pNode->pParent->pParent;
}
else
{
if(_pNode->pParent->pRight == _pNode)
{
left_rotate(_pNode->pParent);
y = _pNode;
_pNode = _pNode->pParent;
}
y->color = BLACK;
y->pParent->color = RED;
right_rotate(y->pParent);
_pNode = y;
}
}
else if(_pNode->pParent == _pNode->pParent->pParent->pRight)
{
y = _pNode->pParent->pParent->pLeft;
if(y ->color == RED)
{
_pNode->pParent->color = BLACK;
_pNode->pParent->pParent->color = RED;
y->color = BLACK;
_pNode = _pNode->pParent->pParent;
}
else
{
y = _pNode->pParent;
if(_pNode->pParent->pLeft == _pNode)
{
y = _pNode;
_pNode = _pNode->pParent;
right_rotate(_pNode);
}
y->color = BLACK;
y->pParent->color = RED;
left_rotate(y->pParent);
_pNode = y;
}
}
}
m_pRoot->color = BLACK;
}
完整模板代码:
#include <iostream>
#include <stack>
namespace DataStructure
{
typedef enum{RED,BLACK} Color;
template<typename T>
class RBTreeNode
{
public:
RBTreeNode()
{
pLeft = pRight = pParent = NULL;
}
RBTreeNode(T _data):data(_data)
{
pLeft = pRight = pParent = NULL;
}
public:
T data;
RBTreeNode<T> *pLeft;
RBTreeNode<T> *pRight;
RBTreeNode<T> *pParent;
Color color;
};
/**
*To be a r&b tree, several characteristics must be satisfied:
* 1. for each node, it must be black or red
* 2. root must be black
* 3. leaf node must be black
* 4. if one node is red, then two sons must be black
* 5. for each node, from which to each leaf, you will get the same number of black nodes,
*/
template<typename T>
class RBTree
{
public:
typedef T dateType;
typedef RBTreeNode<T> nodeType;
public:
RBTree()
{
m_pRoot = NULL;
nil.pParent = nil.pRight = nil.pLeft = NULL;
nil.color = BLACK;
}
//insert a new node into the rb tree
void insert(T data);
//remove a node from the rb tree
void remove(RBTreeNode<T> *_removeNode);
//left rotate
void left_rotate(RBTreeNode<T> *_pNode);
//right rotate
void right_rotate(RBTreeNode<T>* _pNode);
//find the tree node
RBTreeNode<T>* find(T _data)const;
//print all elements
void print_elements()const;
//insert fix up
void insert_fix_up(RBTreeNode<T> *_pNode);
private:
RBTreeNode<T> *m_pRoot;/*! the root of the R&B tree*/
RBTreeNode<T> nil;
};
template<typename T>
void RBTree<T>::print_elements()const
{
if(m_pRoot == NULL)
{
return ;
}
std::stack<RBTreeNode<T>*> nStack;
nStack.push(m_pRoot);
RBTreeNode<T> *tmp = m_pRoot;
std::cout<<"All Elements"<<std::endl;
while(!nStack.empty())
{
while(tmp != &nil)
{
tmp = tmp->pLeft;
nStack.push(tmp);
}
nStack.pop();
if(!nStack.empty())
{
tmp = nStack.top();
nStack.pop();
std::cout<<tmp->data<<std::endl;
tmp = tmp->pRight;
nStack.push(tmp);
}
}
}
//find the tree node
template<typename T>
RBTreeNode<T>* RBTree<T>::find(T _data)const
{
RBTreeNode<T>* pElement = m_pRoot;
while(pElement != &nil && pElement->data != _data)
{
if(_data > pElement->data)
{
pElement = pElement->pRight;
}
else
{
pElement = pElement->pLeft;
}
}
if(pElement != &nil)
{
return pElement;
}
return NULL;
}
//insert a new node into the rb tree
template<typename T>
void RBTree<T>::insert(T data)
{
//create a new node
RBTreeNode<T>* newNode = new RBTreeNode<T>(data);
newNode->color = RED;
newNode->pParent = newNode->pRight = newNode->pLeft = &nil;
//if current tree is empty
if(m_pRoot == NULL)
{
m_pRoot = newNode;
m_pRoot -> color = BLACK;
m_pRoot->pParent = &nil;
return ;
}
//pNode for iterating
//pInsert for insertions
RBTreeNode<T> *pNode = m_pRoot;
RBTreeNode<T> *pInsert = &nil;
while(pNode != &nil)
{
if(data > pNode->data)
{
pInsert = pNode;
pNode = pNode->pRight;
}
else if(data < pNode->data)
{
pInsert = pNode;
pNode = pNode->pLeft;
}
else
{
return ;
}
}
if(pInsert != &nil)
{
if(data > pInsert->data)
{
pInsert->pRight = newNode;
}
else
{
pInsert->pLeft = newNode;
}
newNode->pParent = pInsert;
insert_fix_up(newNode);
}
}
template <typename T>
void RBTree<T>::insert_fix_up(RBTreeNode<T> *_pNode)
{
RBTreeNode<T>* y;
while(_pNode->pParent->color == RED)
{
if(_pNode->pParent == _pNode->pParent->pParent->pLeft)
{
y = _pNode->pParent;
if(y->pParent->pRight->color == RED)
{
_pNode->pParent->pParent->pRight->color = BLACK;
_pNode->pParent->color = BLACK;
_pNode->pParent->pParent->color = RED;
_pNode = _pNode->pParent->pParent;
}
else
{
if(_pNode->pParent->pRight == _pNode)
{
left_rotate(_pNode->pParent);
y = _pNode;
_pNode = _pNode->pParent;
}
y->color = BLACK;
y->pParent->color = RED;
right_rotate(y->pParent);
_pNode = y;
}
}
else if(_pNode->pParent == _pNode->pParent->pParent->pRight)
{
y = _pNode->pParent->pParent->pLeft;
if(y ->color == RED)
{
_pNode->pParent->color = BLACK;
_pNode->pParent->pParent->color = RED;
y->color = BLACK;
_pNode = _pNode->pParent->pParent;
}
else
{
y = _pNode->pParent;
if(_pNode->pParent->pLeft == _pNode)
{
y = _pNode;
_pNode = _pNode->pParent;
right_rotate(_pNode);
}
y->color = BLACK;
y->pParent->color = RED;
left_rotate(y->pParent);
_pNode = y;
}
}
}
m_pRoot->color = BLACK;
}
//remove a node from the rb tree
template<typename T>
void RBTree<T>::remove(RBTreeNode<T> *_removeNode)
{
}
//left rotate
//
template<typename T>
void RBTree<T>::left_rotate(RBTreeNode<T> *_pNode)
{
RBTreeNode<T> *pRightSon = _pNode->pRight;
if(pRightSon == &nil)
{
return ;
}
//process the parent of _pNode
if(_pNode->pParent != &nil)
{
if(_pNode->pParent->pLeft == _pNode)
{
_pNode->pParent->pLeft = pRightSon;
pRightSon->pParent = _pNode->pParent;
}
else if(_pNode->pParent->pRight == _pNode)
{
_pNode->pParent->pRight = pRightSon;
pRightSon->pParent = _pNode->pParent;
}
}
//in this situation, parent of _pNode is the root
else
{
m_pRoot = pRightSon;
pRightSon->pParent = &nil;
}
RBTreeNode<T> *pTmp = _pNode->pLeft;
pRightSon->pLeft = _pNode;
_pNode->pParent = pRightSon;
_pNode->pRight = pTmp;
pTmp->pParent = _pNode;
}
//right rotate
template<typename T>
void RBTree<T>::right_rotate(RBTreeNode<T>* _pNode)
{
RBTreeNode<T> *pLeftSon = _pNode->pLeft;
if(pLeftSon == &nil)
{
return ;
}
if(_pNode->pParent != &nil)
{
if(_pNode->pParent->pLeft == _pNode)
{
_pNode->pParent->pLeft = pLeftSon;
pLeftSon->pParent = _pNode->pParent;
}
else
{
_pNode->pParent->pRight = pLeftSon;
pLeftSon->pParent = _pNode->pParent;
}
}
else
{
m_pRoot = pLeftSon;
pLeftSon->pParent = &nil;
}
//process the parental relation between _pNode and pLeftSon
RBTreeNode<T> *pTmp = pLeftSon->pRight;
pLeftSon->pRight = _pNode;
_pNode->pParent = pLeftSon;
_pNode->pLeft = pTmp;
pTmp->pParent = _pNode;
}
}