您的位置:首页 > 理论基础 > 数据结构算法

【数据结构】红黑树(如何实现及怎样判断)

2016-06-16 16:02 330 查看
红黑树是一颗二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是red或black。通过对任何一条从根节点到叶子节点的简单路径上的颜色来约束,红黑树保证了最长路径不超过最短路经的两倍,因此近似于平衡。

红黑树的规则:

1、每个节点不是红色就是黑色的。

2、根结点是黑色的。

3、如果一个节点是红色的,则它的两个子结点是黑色的。即每条路径上不能存在两个连续的红节点。

4、对每个节点,从该节点到其他节点的简单路径上,均包含相同数目的黑色节点。

红黑树节点RBTreeNode的实现,利用三叉链(left、right、parent)、key、value及颜色col。

enum colour
{
RED,
BLACK,
};
template<class K, class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
K _key;
V _value;
colour _col;
RBTreeNode(const K& key = K(), const V& value = V())
:_left(NULL)
, _right(NULL)
, _parent(NULL)
, _key(key)
, _value(value)
, _col(RED)//初始化插入节点的颜色为红色,不影响黑节点个数
{}
};

一、红黑树的插入

红黑树的插入类似于二叉搜索树,但是每次插入后都到要注意是否满足红黑树的规则,特别是规则3和规则4。如果不满足就需要调整树的结构,下面对插入节点时分成以下几种情况:

注:cur(插入节点)parent(cur的父亲节点)grandfather(parent的父亲节点)uncle(父亲的兄弟节点)

1、根节点root为空,直接插入新节点并给root,设置根节点的颜色为BLACK。

2、根节点root不为空,找到插入节点的位置并插入节点cur。cur节点是红色,若parent是红节点,则需要进行调整。

存在以下三种情况:

情况一:cur为红,parent为红,grandfather为黑,uncle存在且为红。

调整方案,如下如所示:



情况二:cur为红,parent为红,grandfather为黑,uncle不存在或者uncle为黑。

左单旋:parent为grantfather的右孩子,cur为parent的右孩子。

右单旋: parent为grantfather的左孩子,cur为parent的左孩子。

调整方案,下面以右单旋进行分析,如下图所示:



左单旋转类似右单旋转的实现过程。

情况三:cur为红,parent为红,grantfather为黑,uncle不存在或者uncle为黑。

左右单旋:parent为grantfather的右孩子,cur为parent的左孩子。

右左单旋:parent为grantfather的左孩子,cur为parent的右孩子。

调整方案,下面以做右单旋进行分析,如下图所示:



右左旋转的实现类似左单旋转的实现过程。

具体实现如下:

RBTree()
 :_root(NULL)
{}
bool Insert(const K& key, const V& value)
{
 //1、_root为空时,插入节点是根节点
 if (_root == NULL)
 {
  _root = new Node(key, value);
  _root->_col = BLACK;
  return true;
 }
 Node* parent = NULL;
 Node* cur = _root;
 while (cur)//找到插入节点的位置
 {
  if (cur->_key > key)
  {
   parent = cur;
   cur = cur->_left;
  }
  else if (cur->_key < key)
  {
   parent = cur;
   cur = cur->_right;
  }
  else
   return false;
 }
 cur = new Node(key, value);//插入节点
 if (parent->_key > cur->_key)
 {
  parent->_left = cur;
  cur->_parent = parent;
 }
 if (parent->_key < cur->_key)
 {
  parent->_right = cur;
  cur->_parent = parent;
 }
 //2、出现两个连续红节点,进行调整
 while (cur != _root && parent->_col == RED)//此条件说明存在parent节点,parent存在父亲节点
 {
  Node* grandfather = parent->_parent;
  if (parent == grandfather->_left)
  {
   Node* uncle = grandfather->_right;
   if (uncle && uncle->_col == RED)//情况一:uncle为RED
   {
    grandfather->_col = RED;
    parent->_col = BLACK;
    uncle->_col = BLACK;
   }
   else //情况二、三:uncle为BLACK或不存在(右单旋或左右单旋)
   {//先考虑左右单旋,先进行左单旋,转化为情况二,再进行右单旋
    if (cur == parent->_right)
    {
     _RotateL(parent);//左旋不需要变颜色
    }
    _RotateR(grandfather);
   }
  }
  else
  {
   Node* grandfather = parent->_parent;
   if (parent == grandfather->_right)
   {
    Node* uncle = grandfather->_left;
    if (uncle && uncle->_col == RED)//情况一:uncle为RED
    {
     grandfather->_col = RED;
     parent->_col = BLACK;
     uncle->_col = BLACK;
    }
    else //情况二、三:uncle为BLACK或不存在(左单旋或右左单旋)
    {
     if (cur == parent->_left)
     {
      _RotateR(parent);//右旋不需要变颜色
     }
     _RotateL(grandfather);
    }
   }
  }
  cur = grandfather;
  parent = cur->_parent;
  _root->_col = BLACK;
 }
}
void _RotateL(Node* parent)
{
 Node* SubR = parent->_right;
 Node* SubRL = SubR->_left;
 parent->_right = SubRL;
 if (SubRL)
  SubRL->_parent = parent;
 SubR->_parent = parent->_parent;
 SubR->_left = parent;
 parent->_parent = SubR;
 //变色
 SubR->_col = BLACK;//情况二中:parent变黑,grandfather变红
 parent->_col = RED;
 parent = SubR;
 if (parent->_parent == NULL)
  _root = parent;
 else
 {
  Node* ppNode = parent->_parent;
  if (ppNode->_key > parent->_key)
   ppNode->_left = parent;
  else
   ppNode->_right = parent;
 }
}
void _RotateR(Node* parent)
{
 Node* SubL = parent->_left;
 Node* SubLR = SubL->_right;
 parent->_left = SubLR;
 if (SubLR)
  SubLR->_parent = parent;
 SubL->_parent = parent->_parent;
 SubL->_right = parent;
 parent->_parent = SubL;
 //变色
 SubL->_col = BLACK;//情况二中:parent变黑,grandfather变红
 parent->_col = RED;
parent = SubL;
 if (parent->_parent == NULL)
  _root = parent;
 else
 {
  Node* ppNode = parent->_parent;
  if (ppNode->_key > parent->_key)
   ppNode->_left = parent;
  else
   ppNode->_right = parent;
 }
}


二、红黑树的判断

1、根结点是否满足红黑树规则,是否为黑色。

2、每条路径的黑色节点相等。统计出一条路径的黑色节点的个数,然后与其他路径黑色节点个数进行比较。

3、不存在连续的红色节点,判断红色节点的父亲节点是否为红色。

具体实现如下:

bool Check()
{
if (_root->_col == RED)
return false;
int count = 0;//统计出一条路径的黑色节点的个数
int num = 0;//需要与count比较的其他路径黑色节点个数
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
count++;
cur = cur->_left;
}
return _Check(_root, count, num);
}
bool _Check(Node* root, int BlackNum, int CurBlackNum)
{
 if (root == NULL)
  return true;
 if (root->_col == RED && root->_parent->_col == RED)//存在两个连续的红节点
  return false;
 if (root->_col == BLACK)//黑节点就CurBlackNum++
  CurBlackNum++;
 if (root->_left == NULL && root->_right == NULL)
 {
  if (CurBlackNum == BlackNum)
   return true;
  else//黑色节点不相等返回false
   return false;
 }
 return _Check(root->_left, BlackNum, CurBlackNum)
  && _Check(root->_right, BlackNum, CurBlackNum);//进行左右递归
}
以上为个人学习的一些总结,如有纰漏,请多多指教。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: