RBTree 红黑树图解!可以说是很详细了

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是RedBlack

通过对任何一条从根到叶子节点简单路径上的颜色来约束树的高度,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。

红黑树是满足下面红黑性质的二叉搜索树:

1. 每个节点,不是红色就是黑色的

2. 根节点是黑色的

3. 如果一个节点是红色的,则它的两个子节点是黑色的(不存在连续的红色节点)

4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。

 

思考:为什么满足上面的颜色约束性质,红黑树能保证最长路径不超过最短路径的两倍?

  最短的路径上节点的颜色全部都为黑色;最长的路径则为黑红交叉的路径,其上有与最短路径的黑节点数目相同的黑节点数和红节点数目。所以我们按照红黑树性质所建立的红黑树的最长路径必然不会超过最短路径的两倍!

建立红黑树的节点类:

插入的新节点默认是红色的。原因是:插入黑节点必然会影响所有路径都含有相同数目的黑色节点这一原则,较难维护!

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 enum  Color {      RED,      BLACK };   template < class  K, class  V> struct  RBTreeNode {      K _key;      V _value;      Color _color;    //颜色      RBTreeNode<K, V>* _left;       //指向左孩子的指针      RBTreeNode<K, V>* _right;  //指向右孩子的指针      RBTreeNode<K, V>* _parent;     //指向父节点的指针        RBTreeNode( const  K& key=K(),  const  V&value=V())          :_key(key)          , _value(value)          , _color(RED)          , _left(NULL)          , _right(NULL)          , _parent(NULL)      {} };

  红黑树需要变色或利用旋转来降低高度的几种情况:

图注:g代表grandfather祖父节点;p代表parent父亲结点;u代表uncle叔叔节点;cur代表当前节点

一、父节点是祖父节点的左孩子

1.uncle的颜色是红色

①当前节点cur是parent的左孩子

《RBTree 红黑树图解!可以说是很详细了》

②当前节点cur是parent的右孩子

 《RBTree 红黑树图解!可以说是很详细了》

2.uncle的颜色是黑色 或者 uncle为NULL

①cur是parent的左孩子,右单旋

《RBTree 红黑树图解!可以说是很详细了》

②cur是parent的右孩子,先左后右双旋

《RBTree 红黑树图解!可以说是很详细了》

二、父节点是祖父节点的右孩子

1.uncle的颜色是红色

①cur是parent的右孩子

《RBTree 红黑树图解!可以说是很详细了》

②cur是parent的左孩子

《RBTree 红黑树图解!可以说是很详细了》

2.uncle的颜色是黑色 或者 uncle为NULL

①cur是parent的右孩子

《RBTree 红黑树图解!可以说是很详细了》

②cur是parent的左孩子

《RBTree 红黑树图解!可以说是很详细了》

插入节点:

  1. 首先,要找到插入该节点的位置,找到后就插入节点
  2. 然后,对红黑树的节点颜色的合法性进行检查,并根据检查结果进行变色或者旋转。

基于以上的情况,红黑树利用模板类封装的插入函数算法就完成了:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 template < class  K,  class  V> bool  RBTree<K, V>::Insert( const  K& key,  const  V& value) {      if  (_root == NULL)      {          _root =  new  RBTreeNode<K, V>(key, value);          _root->_color = BLACK;          return  true ;      }      //  找位置      RBTreeNode<K, V>* cur = _root;      RBTreeNode<K, V>* parent = NULL;      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  RBTreeNode<K, V>(key, value);      cur->_parent = parent;      if  (parent->_key > key)          parent->_left = cur;      else  if  (parent->_key < key)          parent->_right = cur;            //检查颜色分配是否满足要求      while  (parent&&parent->_color==RED)      {          RBTreeNode<K, V>* grandfather = parent->_parent;          if  (parent == grandfather->_left)          {              RBTreeNode<K, V>* uncle = grandfather->_right;              if  (uncle&&uncle->_color == RED)              {    //第一种情况  变色                  grandfather->_color = RED;                  parent->_color =BLACK;                  uncle->_color = BLACK;                                    cur = grandfather;                  parent = grandfather->_parent;              }              else  if ( (uncle&&uncle->_color==BLACK)||uncle==NULL)              {                    if  (cur == parent->_left)                  { //第二种情况 右单旋        cur必然有黑色孩子                      parent->_color = BLACK;                      grandfather->_color = RED;                      RotateR(grandfather);                  }                  else                  { //第三种情况 左右双旋                      RotateL(parent);                      parent->_color = BLACK;                      grandfather->_color = RED;                      RotateR(grandfather);                  }                  break ;              }          }          else  if  (parent == grandfather->_right)          {              RBTreeNode<K, V>* uncle = grandfather->_left;              if  (uncle&&uncle->_color == RED)              { //第一种情况 变色                  grandfather->_color = RED;                  parent->_color = BLACK;                  uncle->_color = BLACK;                                    cur = grandfather;                  parent = cur->_parent;              }              else  if ( (uncle&&uncle->_color == BLACK)||uncle==NULL)              { //第二种情况 左单旋 cur必然有黑孩子                  if  (cur == parent->_right)                  {                      parent->_color = BLACK;                      grandfather->_color = RED;                      RotateL(grandfather);                  }                  else  if  (cur==parent->_left)                  { //第三种情况 右左双旋                      RotateR(parent);                      parent->_color = BLACK;                      grandfather->_color = RED;                        RotateL(grandfather);                  }                  break ;              }          }      }      _root->_color = BLACK;      return  true ; }

  插入完成之后,我们无法直观的看出红黑树的节点颜色是否合法,也无法直观的看出每条路径的黑色节点数目是否相同。

所以,这里实现两个函数,方便检验红黑树的合法性。

  • 红黑树每条路径的黑色节点数目都相同,所以随意遍历一条路径,计算这条路上的黑色节点的数目。以该数据为标杆,和其他路径的黑色节点数目作比较,判断是否都相同。
  • 如果当前节点是红颜色并且它有父节点,那么再判断父节点的颜色是否也是红色,这样就能判断该树是否满足连续两个节点不能同时为红色这一性质。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 //检验红黑树的合法性 template < class  K,  class  V> bool  RBTree<K, V>::Check() {      //统计红黑树每条路径上黑色节点的数量      int  blackNum = 0;      RBTreeNode<K, V>* cur = _root;      while  (cur)      {          if  (cur->_color == BLACK)              blackNum++;          cur = cur->_left;      }      int  CBNum = 0;      return  _Check(_root,blackNum,CBNum); }   ////////////////// 递归辅助 template < class  K,  class  V> bool  RBTree<K, V>::_Check(RBTreeNode<K, V>* root,  int  blackNum,  int  CBNum) {      if  (root == NULL)          return  true ;      if  (root->_color == BLACK)      {          CBNum++;          if  (root->_left == NULL&&root->_right == NULL)          {    //走到了叶子节点 将该条路径上的黑色节点数量与之前统计的黑色节点数量做比较              if  (blackNum == CBNum)              {                  return  true ;              }              else              {                  cout <<  "叶子节点为"  << root->_key <<  "路径的黑色节点数目与最左侧支路的黑色节点数目不相等 !"  << endl;                  return  false ;              }          }      }      else  if  (root->_parent&&root->_parent->_color == RED)      { //判断是否存在连续的两个红色节点          cout << root->_parent->_key <<  " 和 "  << root->_key <<  " 为两个连续的红色节点"  << endl;          return  false ;      }      //递归检验子路      return  _Check(root->_left, blackNum, CBNum) && _Check(root->_right, blackNum, CBNum); }

  红黑树和AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(lg(N)) 红黑树的不追求完全平衡,保证最长路径不超过最短路径的2倍,相对而言,降低了旋转的要求,所以性能会优于AVL树,所以实际运用 中红黑树更多。

    原文作者:算法小白
    原文地址: https://blog.csdn.net/Gease_lcj/article/details/79233761
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞