天天看點

C++實作紅黑樹

一、紅黑樹的性質

紅黑樹也是二叉搜尋樹,它的每個節點增加了一個機關用來表示顔色,可以是Red也可以是Black,通過對任意一條根到葉子節點的顔色來限制,紅黑樹保證最長路徑是最短路徑的兩倍,是以近似平衡。

紅黑樹的性質:

1、每個節點不是紅色就是黑色

2、樹的根節點是黑色

3、如果一個節點是紅色,則它的兩個孩子節點是黑色的(沒有連續的紅色)

4、 對于每個結點,從該結點到其所有後代葉結點的簡單路徑上,均包含相同數目的黑色結點(每條路徑上黑色結點的數量相等)

看一下紅黑樹的節點結構:

enum COLOR
{
  BLACK,
  RED
};

template<class K, class V>
struct RBTreeNode
{
  RBTreeNode(const K& key, const V& value, COLOR color = RED)
  :_pLeft(NULL)
  , _pRight(NULL)
  , _pParent(NULL)
  , _key(key)
  , _value(value)
  , _color(color)
  {}

  RBTreeNode<K, V> *_pLeft;
  RBTreeNode<K, V> *_pRight;
  RBTreeNode<K, V> *_pParent;

  K _key;
  V _value;
  COLOR _color;
};      

二、插入節點後分析

1、插入的位置若為根節點,根據性質可知調整為黑色。

2、插入節點後,違反了性質,需要做調整,可能遇到下面三種情況:

注:cur為目前節點,p為父節點,g為祖父節點,u為叔叔節點

情況一:

cur為紅,p為紅,g為黑,u存在且為紅,如下圖:

C++實作紅黑樹

不能将p直接改為黑色,這樣會改變某路徑黑色節點的個數。

情況二:

cur為紅,p為紅,g為黑,u不存在/u為黑,如下圖,對樹g右旋:

C++實作紅黑樹

 上圖,p為g的左孩子,cur為p的左孩子,則進行右單旋轉;同理,p為g的右孩子,cur 為p的右孩子,則進行左單旋轉。

注:左旋及右旋的具體過程和AVL樹很像,隻有少了平衡因子的更新,但是多了變色處理。具體可以參考上篇AVL樹。

情況三:

cur為紅,p為紅,g為黑,u不存在/u為黑,如下圖,對樹cur左旋:

C++實作紅黑樹

轉化為情況二,對g進行右旋。

上圖,p為g的左孩子,cur為p的右孩子,則針對p做左單旋轉;同理,p為g的右孩子, cur為p的左孩子,則針對p做右單旋轉。

插入算法代碼如下:

bool Insert(const K& key, const V& value)
  {
    if (NULL == _pRoot)
    {
      _pRoot = new Node(key, value, BLACK);   //根節點為黑色
      return true;
    }

    //找插入位置
    pNode pCur = _pRoot;
    pNode pParent = NULL;

    while (pCur)
    {
      if (key < pCur->_key)
      {
        pParent = pCur;
        pCur = pCur->_pLeft;
      }
      else if (key > pCur->_key)
      {
        pParent = pCur;
        pCur = pCur->_pRight;
      }
      else
      {
        return false;
      }
    }

    pCur = new Node(key, value);
    if (key < pParent->_key)
      pParent->_pLeft = pCur;
    else
      pParent->_pRight = pCur;

    pCur->_pParent = pParent;

    //有可能會違反紅黑樹的性質---3
    while (pParent&&pParent->_color == RED)
    {
      pNode grandParent = pParent->_pParent;
      if (pParent == grandParent->_pLeft)
      {
        pNode uncle = grandParent->_pRight;
        //情況一,叔叔節點存在且為紅
        if (uncle&&uncle->_color == RED)
        {
          pParent->_color = BLACK;
          uncle->_color = BLACK;
          grandParent->_color = RED;

          pCur = grandParent;   //繼續向上調整
          pParent = pCur->_pParent;
        }

        //叔叔不存在或者存在且為黑
        else
        {
          //若是第三種情況轉化為第二種
          if (pCur == pParent->_pRight)
          {
            RotateL(pParent);
            swap(pParent, pCur);
          }

          RotateR(grandParent);
          pParent->_color = BLACK;
          grandParent->_color = RED;
          break;
        }
      }
      //父親節點為雙親的右節點
      else
      {
        pNode uncle = grandParent->_pLeft;
        //情況一,叔叔節點存在且為紅
        if (uncle&&uncle->_color == RED)
        {
          pParent->_color = BLACK;
          uncle->_color = BLACK;
          grandParent->_color = RED;

          pCur = grandParent;   //繼續向上調整
          pParent = pCur->_pParent;
        }

        //叔叔不存在或者存在且為黑
        else
        {
          //若是第三種情況轉化為第二種
          if (pCur == pParent->_pLeft)
          {
            RotateR(pParent);
            swap(pParent, pCur);
          }

          RotateL(grandParent);
          pParent->_color = BLACK;
          grandParent->_color = RED;
        }
      }
    }
    _pRoot->_color = BLACK;
    return true;
  }      

三、判斷是否為紅黑樹

根據紅黑樹的性質,根節點為黑色;要滿足任何一個路徑黑色節點的個數相等, 我們可以分别統計每條黑色節點的個數,當相等時就判斷是不是紅黑樹 ;以及有沒有相鄰的紅色節點。

bool IsRBTree()
  {
    if (_pRoot == NULL)
      return true;
    if (_pRoot->_color == RED)  //根節點為紅色
      return false;

    int blackNum = 0;  //黑色節點的個數
    int num = 0;     //任意路徑的黑色節點個數
    pNode pCur = _pRoot;
    while (pCur)
    {
      if (pCur->_color == BLACK)
      {
        blackNum++;
      }
      pCur = pCur->_pLeft;
    }

    return _IsRBTree(_pRoot, blackNum, num);
  }      
bool _IsRBTree(pNode pRoot, const int blackNum, int num)
  {
    if (NULL == pRoot)  //一條路徑結束
    {
      if (num != blackNum)
      {
        return false;
      }
      else
      {
        return true;
      }
    }
    if (pRoot->_color == BLACK)
    {
      ++num;
    }
    //兩個相鄰的紅色節點
    if ((pRoot->_color == RED) && (pRoot->_pParent->_color == RED))
    {
      return false;
    }
    return _IsRBTree(pRoot->_pLeft, blackNum, num) && _IsRBTree(pRoot->_pRight, blackNum, num);
  }      
#pragma once

enum COLOR
{
  BLACK,
  RED
};

template<class K, class V>
struct RBTreeNode
{
  RBTreeNode(const K& key, const V& value, COLOR color = RED)
  :_pLeft(NULL)
  , _pRight(NULL)
  , _pParent(NULL)
  , _key(key)
  , _value(value)
  , _color(color)
  {}

  RBTreeNode<K, V> *_pLeft;
  RBTreeNode<K, V> *_pRight;
  RBTreeNode<K, V> *_pParent;

  K _key;
  V _value;
  COLOR _color;
};

template<class K, class V>
class RBTree
{
  typedef RBTreeNode<K, V> Node;
  typedef Node* pNode;
public:
  RBTree()
    :_pRoot(NULL)
  {}

  bool Insert(const K& key, const V& value)
  {
    if (NULL == _pRoot)
    {
      _pRoot = new Node(key, value, BLACK);   //根節點為黑色
      return true;
    }

    //找插入位置
    pNode pCur = _pRoot;
    pNode pParent = NULL;

    while (pCur)
    {
      if (key < pCur->_key)
      {
        pParent = pCur;
        pCur = pCur->_pLeft;
      }
      else if (key > pCur->_key)
      {
        pParent = pCur;
        pCur = pCur->_pRight;
      }
      else
      {
        return false;
      }
    }

    pCur = new Node(key, value);
    if (key < pParent->_key)
      pParent->_pLeft = pCur;
    else
      pParent->_pRight = pCur;

    pCur->_pParent = pParent;

    //有可能會違反紅黑樹的性質---3
    while (pParent&&pParent->_color == RED)
    {
      pNode grandParent = pParent->_pParent;
      if (pParent == grandParent->_pLeft)
      {
        pNode uncle = grandParent->_pRight;
        //情況一,叔叔節點存在且為紅
        if (uncle&&uncle->_color == RED)
        {
          pParent->_color = BLACK;
          uncle->_color = BLACK;
          grandParent->_color = RED;

          pCur = grandParent;   //繼續向上調整
          pParent = pCur->_pParent;
        }

        //叔叔不存在或者存在且為黑
        else
        {
          //若是第三種情況轉化為第二種
          if (pCur == pParent->_pRight)
          {
            RotateL(pParent);
            swap(pParent, pCur);
          }

          RotateR(grandParent);
          pParent->_color = BLACK;
          grandParent->_color = RED;
          break;
        }
      }
      //父親節點為雙親的右節點
      else
      {
        pNode uncle = grandParent->_pLeft;
        //情況一,叔叔節點存在且為紅
        if (uncle&&uncle->_color == RED)
        {
          pParent->_color = BLACK;
          uncle->_color = BLACK;
          grandParent->_color = RED;

          pCur = grandParent;   //繼續向上調整
          pParent = pCur->_pParent;
        }

        //叔叔不存在或者存在且為黑
        else
        {
          //若是第三種情況轉化為第二種
          if (pCur == pParent->_pLeft)
          {
            RotateR(pParent);
            swap(pParent, pCur);
          }

          RotateL(grandParent);
          pParent->_color = BLACK;
          grandParent->_color = RED;
        }
      }
    }
    _pRoot->_color = BLACK;
    return true;
  }

  bool IsRBTree()
  {
    if (_pRoot == NULL)
      return true;
    if (_pRoot->_color == RED)  //根節點為紅色
      return false;

    int blackNum = 0;  //黑色節點的個數
    int num = 0;     //任意路徑的黑色節點個數
    pNode pCur = _pRoot;
    while (pCur)
    {
      if (pCur->_color == BLACK)
      {
        blackNum++;
      }
      pCur = pCur->_pLeft;
    }

    return _IsRBTree(_pRoot, blackNum, num);
  }
  void InOrder()
  {
    if (NULL == _pRoot)
      return;
    _InOrder(_pRoot);
  }
private:
  bool _IsRBTree(pNode pRoot, const int blackNum, int num)
  {
    if (NULL == pRoot)  //一條路徑結束
    {
      if (num != blackNum)
      {
        return false;
      }
      else
      {
        return true;
      }
    }
    if (pRoot->_color == BLACK)
    {
      ++num;
    }
    //兩個相鄰的紅色節點
    if ((pRoot->_color == RED) && (pRoot->_pParent->_color == RED))
    {
      return false;
    }
    return _IsRBTree(pRoot->_pLeft, blackNum, num) && _IsRBTree(pRoot->_pRight, blackNum, num);
  }
  //左單旋
  void RotateL(pNode pParent)
  {
    pNode pSubR = pParent->_pRight;
    pNode pSubRL = pSubR->_pLeft;

    //處理pSubR
    pParent->_pRight = pSubRL;
    if (pSubRL)
      pSubRL->_pParent = pParent;

    pSubR->_pLeft = pParent;
    pNode pPParent = pParent->_pParent;
    pSubR->_pParent = pPParent;
    pParent->_pParent = pSubR;

    //判斷是否為根
    if (pParent == _pRoot)
      _pRoot = pSubR;
    //判斷是左子樹還是右子樹
    else
    {
      if (pPParent->_pLeft == pParent)
        pPParent->_pLeft = pSubR;
      else
        pPParent->_pRight = pSubR;
    }
  }

  //右單旋
  void RotateR(pNode pParent)
  {
    pNode pSubL = pParent->_pLeft;
    pNode pSubLR = pSubL->_pRight;

    //處理pSubR
    pParent->_pLeft = pSubLR;
    if (pSubLR)
      pSubLR->_pParent = pParent;

    pSubL->_pRight = pParent;
    pNode pPParent = pParent->_pParent;
    pSubL->_pParent = pPParent;
    pParent->_pParent = pSubL;

    //判斷是否為根
    if (pParent == _pRoot)
      _pRoot = pSubL;
    //判斷是左子樹還是右子樹
    else
    {
      if (pPParent->_pLeft == pParent)
        pPParent->_pLeft = pSubL;
      else
        pPParent->_pRight = pSubL;
    }
  }

  void _InOrder(pNode pRoot)
  {
    if (pRoot)
    {
      _InOrder(pRoot->_pLeft);
      cout << "<" << pRoot->_key << "," << pRoot->_color << ">" << endl;
      _InOrder(pRoot->_pRight);
    }
  }
private:
  pNode _pRoot;
};

void RBTreeTest()
{
  int array[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
  RBTree<int, int> t;
  for (size_t i = 0; i < sizeof(array) / sizeof(array[0]); i++)
  {
    t.Insert(array[i], i);
  }
  t.InOrder();

  if (t.IsRBTree())
    cout << "是紅黑樹" << endl;
  else
    cout << "不是紅黑樹" << endl;
}