红黑树的实现

简介: 红黑树的实现

1 红黑树的性质


1️⃣一个节点不是红节点就是黑节点

2️⃣根节点一定是黑节点

3️⃣不存在连续的红节点

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

5️⃣每个叶子结点都是黑色的(此处的叶子结点指的是空结点)


通过以上几点性质,我们可以知道红黑树的核心思想就是最长路径最多为最短路径的两倍,并且此时在最极端的情况下,节点全部为黑色的路径是最短的,最长的就是一黑一红交错着的!与AVL树差不多,这里我们直接给出红黑树节点的定义:

//利用枚举,来表示两种颜色
enum Colour
{
  RED,
  BLACK
};

template <class K,class V>
struct RBTreeNode
{ 
  RBTreeNode<K, V>* _left;
  RBTreeNode<K, V>* _right;
  RBTreeNode<K, V>* _parent;
  Colour _col;
  pair<K, V> _kv;

  RBTreeNode(pair<K,V> kv)
    :_left(nullptr),
    _right(nullptr),
    _parent(nullptr),
    _col(RED),
    _kv(kv)
  {}

};

2 红黑树的插入

首先,对于新插入进来的节点是红色节点还是黑色节点!这个我们必须想清楚了,显然,要插入的新节点必须得是红色节点,如果是黑色节点,那么我们就必须在其他路径上也要新增黑色节点,这样才能保证从当前节点到叶子结点的黑色节点个数是一样的!而插入红色节点,就不需要我们考虑其他路径上黑节点是否需要新增的问题了!第二个,如何变色的呢?在红黑树中,新增的节点,其父亲节点,祖父节点的颜色是确定的,如果父亲节点是黑色的,那就不用调整颜色了。但是如果父亲的颜色是红色的,那就需要调整了,而且叔叔节点的颜色是不确定的,所以我们变色的规则就要分开来讨论了!


2.1 叔叔节点存在,且为红色


如下简图所示:

825eb3c3952d4cd0c99436a5c869c7b3_2bb7c10a78ec44469d2e24dc9c1e1ef0.png

此时我们只需要改变颜色就可以了,将parent和uncle的颜色改为黑色节点,grandfather节点的颜色改为红色,在让cur等于grandfather,继续往上进行更新,如果grandfather是根节点,那就需要把根节点的颜色改为黑色!


2.2 叔叔节点不存在,或者存在为黑色节点

此时我们可以看到叔叔节点不存在,或者叔叔节点的颜色是黑色,此时变色解决不了问题了,那么这个时候就需要我们进行旋转加变色就可以解决问题了!这里的旋转就是我们之前AVL树那一节所学过的!只不过里面没有平衡因子的改变了,只需要旋转就行了!图中只是描述了叔叔在右边的情况,当然还有叔叔在左边的情况,左边的情况是与右边的情况是相反的!


3 具体实现

有了上述的思路,我们可以很快的实现红黑树了

bool Insert(pair<K,V> kv)
{
  //1 寻找要插入的位置
  if (_root == nullptr)
  {
    _root = new Node(kv);
    _root->_col = BLACK;
    return true;
  }
  Node* parent = _root;
  Node* cur = _root;

  while (cur)
  {
    if (cur->_kv.first > kv.first)
    {
      parent = cur;
      cur = cur->_left;
    }
    else if (cur->_kv.first < kv.first)
    {
      parent = cur;
      cur = cur->_right;
    }
    else
    {
      return false;
    }
    if (cur)
      cur->_parent = parent;
  }

  cur = new Node(kv);
  if (kv.first > parent->_kv.first)
  {
    parent->_right = cur;
  }
  else {
    parent->_left = cur;
  }
  cur->_parent = parent;

  //开始改变颜色
  while (parent && parent->_col==RED)
  {
    Node* grandfather = parent->_parent;
    
    //parent节点在祖父节点的左边
    if (parent==grandfather->_left)
    {
      //第一种情况,需要变色处理
      Node* uncle = grandfather->_right;
      if (uncle && uncle->_col == RED)
      {
        uncle->_col = BLACK;
        parent->_col = BLACK;
        grandfather->_col = RED;
        cur = grandfather;
        parent = cur->_parent;
      }
      else
      {
        //第二种情况 uncle节点为空或者是黑色节点
        if (cur == parent->_left)
        {
          RotateR(grandfather);
          parent->_col = BLACK;
          grandfather->_col = RED;
        }
        else
        {
          RotateL(parent);
          RotateR(grandfather);
          parent->_col = BLACK;
          grandfather->_col = RED;
        }
        //由于根节点变成了黑色,那就不用再往上判断了
        break;
      }
    }
    else
    {
      Node* uncle = grandfather->_left;
      if (uncle && uncle->_col == RED)
      {
        uncle->_col = BLACK;
        parent->_col = BLACK;
        grandfather->_col = RED;
        cur = grandfather;
        parent = cur->_parent;
      }
      else
      {
        //第二种情况 uncle节点为空或者是黑色节点
        if (cur == parent->_left)
        {
          RotateR(parent);
          RotateL(grandfather);
          parent->_col = BLACK;
          grandfather->_col = RED;
        }
        else
        {
          RotateL(grandfather);
          parent->_col = BLACK;
          grandfather->_col = RED;
        }
        //由于根节点变成了黑色,那就不用再往上判断了
        break;
      }
    }
  }
  //将根节点的颜色变成黑色就行了
  _root->_col = BLACK;

  return true;

}

4 红黑树的验证

1️⃣验证是否符合中序遍历是否有序

2️⃣验证是否满足红黑树的性质


注意,不可以用最长路径不超过最短路径的两倍这一思想来验证,如果你这个红黑树不满足所有性质条件,并且也满足了以上两个条件,那么这样一棵树,并不是红黑树!所以我们验证的思想就是取其中一个路径,遍历一遍,比较其他路径中黑色节点数是否与这条路径上的数量相等!判断子节点与父节点是否为联系的红色节点!具体的实现代码如下所示:

  bool IsRBTree()
  {
    if (_root == nullptr)
      return true;
    Node* tmp = _root;
    int Countnum = 0;
    while (tmp)
    {
      if (tmp->_col == BLACK)
      {
        Countnum++;
      }
      tmp = tmp->_left;
    }
    return check(_root,0,Countnum);
  }
  bool check(Node* root, int comparenum,int  Countnum)
  {
    if (root == nullptr)
    {
      if (Countnum != comparenum)
        return false;
      return true;
    }

    if (root->_col == BLACK)
      comparenum++;
    if (root->_parent)
      if (root->_col == RED && root->_parent->_col == root->_col)
        return false;

    return check(root->_left, comparenum, Countnum) && check(root->_right, comparenum, Countnum);
  }

5 红黑树与AVL树之间的比较

红黑树相比较于AVL树,树的高度会高一点,但是相差不大,可以有效的减少旋转次数!查询的时间复杂度依然是O(lgN)。红黑树的应用比AVL树的应用要广的多,例如:C++ STL库 – map/set、mutil_map/mutil_set,以及Linux内核等,底层所采用的数据结构就是红黑树!

目录
相关文章
|
5天前
|
关系型数据库 容器
红黑树的简单介绍
红黑树的简单介绍
21 0
|
5天前
|
存储 应用服务中间件 调度
随处可见的红黑树详解
随处可见的红黑树详解
44 0
|
5天前
|
存储 调度
红黑树总结
红黑树总结
37 0
|
5天前
|
调度
随处可见的红黑树
随处可见的红黑树
|
6月前
|
算法 Java Linux
C++红黑树
C++红黑树
|
10月前
|
JavaScript
红黑树是怎么来的
本文从二叉搜索树倾斜的原因(自上而下生长)出发,推出维持树形数据结构平衡性的关键:自下而上裂变式生长,进而引出裂变式生长的理论模型:2-3 树。由于 2-3 树实现上的复杂性,引出其实现上的替代品:红黑树。最后,我们讨论如何通过左旋、右旋以及颜色翻转这“三板斧”来维护红黑树插入和删除元素后的动态平衡。
63 2
红黑树是怎么来的
|
11月前
红黑树
红黑树
46 0
|
11月前
|
C++
C++之红黑树
C++之红黑树
60 0
|
Java Linux C++
C++:红黑树
写了红黑树的概念、性质,以及重点分析了红黑树新增节点的情况。
C++:红黑树