C++进阶--红黑树

简介: C++进阶--红黑树

概念与特性

红黑树,是一种自平衡的二叉搜索树,它具有以下特点:

  1. 每个节点要么是红色,要么是黑色
  2. 根节点是黑色的。
  3. 每个叶子节点(NIL节点,空节点)都是黑色的。
  4. 如果一个节点是红色的,那么它的两个子节点都是黑色的,也就说,不存在两个连续的红色节点。
  5. 对于任意节点,从该节点到其后代叶子节点的所有路径上包含相同数目的黑色节点,即保证了红黑树的黑色节点高度相同。

红黑树的定义

enum Colour
{
  RED,
  BLACK
};
template<class K,class V>
struct RBTreeNode
{
  RBTreeNode<K, V>* _left;
  RBTreeNode<K, V>* _right;
  RBTreeNode<K, V>* _parent;
  pair<K, V> _kv;
  Colour _col;
  RBTreeNode(const pair<K, V>& kv)
    :_left(nullptr)
    , _right(nullptr)
    , _parent(nullptr)
    , _kv(kv)
    , _col(RED)
  {
  }
};

红黑树的插入操作

旋转的见:AVL树链接入口

bool Insert(const pair<K, V>& kv)
  {
    if (_root == nullptr)
    {
      _root = new Node(kv);
      _root->_col = BLACK;
      return true;
    }
    //确定插入位置
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
      if (cur->_kv.first < kv.first)
      {
        parent = cur;
        cur = cur->_right;
      }
      else if (cur->_kv.first > kv.first)
      {
        parent = cur;
        cur = cur->_left;
      }
      else
      {
        return false;
      }
    }
    //确定cur节点和p节点的位置关系
    cur = new Node(kv);
    if (parent->_kv.first < kv.first)
    {
      parent->_right = cur;
    }
    else
    {
      parent->_left = cur;
    }
    cur->_parent = parent;
    while (parent && parent->_col == RED)
    {
      Node* grandfather = parent->_parent;
      if (parent == grandfather->_left)
      {
        Node* uncle = grandfather->_right;
        //情况一
        if (uncle && uncle->_col == RED)
        {
          parent->_col = uncle->_col = BLACK;
          grandfather->_col = RED;
          //向上处理
          cur = grandfather;
          parent = cur->_parent;
        }
        else//情况2
        {
          if (cur == parent->_left)
          {
            //      g
            //    p    u
            //  c
            RotateR(grandfather);
            parent->_col = BLACK;
            grandfather->_col = RED;
          }
          else
          {
            //      g
            //    p    u
            //      c
            RotateL(parent);
            RotateR(grandfather);
            cur->_col = BLACK;
            grandfather->_col = RED;
          }
          break;//旋转完的子树的根节点必为黑,这时就不用向上调整处理了
        }
      }
      else
      {
        Node* uncle = grandfather->_left;
        // 情况一:叔叔存在且为红
        if (uncle && uncle->_col == RED)
        {
          // 变色
          parent->_col = uncle->_col = BLACK;
          grandfather->_col = RED;
          // 继续往上处理
          cur = grandfather;
          parent = cur->_parent;
        }
        else//情况2
        {
          if (cur == parent->_right)
          {
            //      g
            //    u    p
            //           c
            RotateL(grandfather);
            parent->_col = BLACK;
            grandfather->_col = RED;
          }
          else
          {
            //      g
            //    u    p
            //        c
            RotateR(parent);
            RotateL(grandfather);
            cur->_col = BLACK;
            grandfather->_col = RED;
          }
          break;//旋转完的子树的根节点必为黑,这时就不用向上调整处理了
        }
      }
    }
    _root->_col = BLACK;
    return true;
  }

验证

void TestRBTree1()
{
  int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
  RBTree<int, int> t;
  for (auto e : a)
  {
    t.Insert(make_pair(e, e));
  }
  t.InOrder();
  cout << t.IsBalance() << endl;
}

bool Check(Node* cur, int blackNum, int refBlackNum)
  {
    if (cur == nullptr)
    {
      //当节点为空时,表示这条路径结束,这时就要对黑色节点进行判断
      if (refBlackNum != blackNum)
      {
        cout << "与其他路径的黑色节点数不相等" << endl;
        return false;
      }
      return true;
    }
    if (cur->_col == RED && cur->_parent->_col == RED)
    {
      cout << cur->_kv.first << "存在连续的红色节点" << endl;
      return false;
    }
    if (cur->_col == BLACK)
    {
      blackNum++;
    }
    return Check(cur->_left,blackNum,refBlackNum) && Check(cur->_right,blackNum,refBlackNum);
  }
  bool IsBalance()
  {
    if (_root && _root->_col == RED)
      return false;
    //统计一条路径的黑色节点数
    int refBlackNum = 0;
    Node* cur = _root;
    while (cur)
    {
      if (cur->_col == BLACK)
      {
        refBlackNum++;
      }
      cur = cur->_left;
    }
    return Check(_root,0,refBlackNum);
  }

红黑树的性能测试

void TestRBTree2()
{
  const int N = 1000000;
  vector<int> v;
  v.reserve(N);
  srand(time(0));
  for (size_t i = 0; i < N; i++)
  {
    v.push_back(rand() + i);
  }
  //测插入所需时间
  size_t begin2 = clock();
  RBTree<int, int> t;
  for (auto a : v)
  {
    t.Insert(make_pair(a, a));
  }
  size_t end2 = clock();
  
  cout << "Insert:" << end2 - begin2 << endl;
  cout << t.IsBalance() << endl;
  //测高度,数量
  cout << "height" << t.Height() << endl;
  cout << "Size:" << t.Size() << endl;
  //测查找时间
  size_t begin1 = clock();
  for (size_t i = 0; i < N; i++)
  {
    t.Find((rand() + i));
  }
  size_t end1 = clock();
  cout << "Find:" << end1 - begin1 << endl;
}

原码

#pragma once
enum Colour
{
  RED,
  BLACK
};
template<class K,class V>
struct RBTreeNode
{
  RBTreeNode<K, V>* _left;
  RBTreeNode<K, V>* _right;
  RBTreeNode<K, V>* _parent;
  pair<K, V> _kv;
  Colour _col;
  RBTreeNode(const pair<K, V>& kv)
    :_left(nullptr)
    , _right(nullptr)
    , _parent(nullptr)
    , _kv(kv)
    , _col(RED)
  {
  }
};
template<class K,class V>
class RBTree
{
  typedef RBTreeNode<K, V> Node;
public:
  bool Insert(const pair<K, V>& kv)
  {
    if (_root == nullptr)
    {
      _root = new Node(kv);
      _root->_col = BLACK;
      return true;
    }
    //确定插入位置
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
      if (cur->_kv.first < kv.first)
      {
        parent = cur;
        cur = cur->_right;
      }
      else if (cur->_kv.first > kv.first)
      {
        parent = cur;
        cur = cur->_left;
      }
      else
      {
        return false;
      }
    }
    //确定cur节点和p节点的位置关系
    cur = new Node(kv);
    if (parent->_kv.first < kv.first)
    {
      parent->_right = cur;
    }
    else
    {
      parent->_left = cur;
    }
    cur->_parent = parent;
    while (parent && parent->_col == RED)
    {
      Node* grandfather = parent->_parent;
      if (parent == grandfather->_left)
      {
        Node* uncle = grandfather->_right;
        //情况一
        if (uncle && uncle->_col == RED)
        {
          parent->_col = uncle->_col = BLACK;
          grandfather->_col = RED;
          //向上处理
          cur = grandfather;
          parent = cur->_parent;
        }
        else//情况2
        {
          if (cur == parent->_left)
          {
            //      g
            //    p    u
            //  c
            RotateR(grandfather);
            parent->_col = BLACK;
            grandfather->_col = RED;
          }
          else
          {
            //      g
            //    p    u
            //      c
            RotateL(parent);
            RotateR(grandfather);
            cur->_col = BLACK;
            grandfather->_col = RED;
          }
          break;//旋转完的子树的根节点必为黑,这时就不用向上调整处理了
        }
      }
      else
      {
        Node* uncle = grandfather->_left;
        // 情况一:叔叔存在且为红
        if (uncle && uncle->_col == RED)
        {
          // 变色
          parent->_col = uncle->_col = BLACK;
          grandfather->_col = RED;
          // 继续往上处理
          cur = grandfather;
          parent = cur->_parent;
        }
        else//情况2
        {
          if (cur == parent->_right)
          {
            //      g
            //    u    p
            //           c
            RotateL(grandfather);
            parent->_col = BLACK;
            grandfather->_col = RED;
          }
          else
          {
            //      g
            //    u    p
            //        c
            RotateR(parent);
            RotateL(grandfather);
            cur->_col = BLACK;
            grandfather->_col = RED;
          }
          break;//旋转完的子树的根节点必为黑,这时就不用向上调整处理了
        }
      }
    }
    _root->_col = BLACK;
    return true;
  }
  void RotateL(Node* parent)
  {
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    parent->_right = subRL;
    if (subRL)
      subRL->_parent = parent;
    subR->_left = parent;
    Node* ppnode = parent->_parent;
    parent->_parent = subR;
    if (parent == _root)
    {
      _root = subR;
      subR->_parent = nullptr;
    }
    else
    {
      if (ppnode->_left == parent)
      {
        ppnode->_left = subR;
      }
      else
      {
        ppnode->_right = subR;
      }
      subR->_parent = ppnode;
    }
  }
  void RotateR(Node* parent)
  {
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    parent->_left = subLR;
    if (subLR)
      subLR->_parent = parent;
    subL->_right = parent;
    Node* ppnode = parent->_parent;
    parent->_parent = subL;
    if (parent == _root)
    {
      _root = subL;
      subL->_parent = nullptr;
    }
    else
    {
      if (ppnode->_left == parent)
      {
        ppnode->_left = subL;
      }
      else
      {
        ppnode->_right = subL;
      }
      subL->_parent = ppnode;
    }
  }
  void _InOrder(Node* root)
  {
    if (root == nullptr)
      return;
    _InOrder(root->_left);
    cout << root->_kv.first << endl;
    _InOrder(root->_right);
  }
  void InOrder()
  {
    _InOrder(_root);
  }
  bool Check(Node* cur, int blackNum, int refBlackNum)
  {
    if (cur == nullptr)
    {
      //当节点为空时,表示这条路径结束,这时就要对黑色节点进行判断
      if (refBlackNum != blackNum)
      {
        cout << "与其他路径的黑色节点数不相等" << endl;
        return false;
      }
      return true;
    }
    if (cur->_col == RED && cur->_parent->_col == RED)
    {
      cout << cur->_kv.first << "存在连续的红色节点" << endl;
      return false;
    }
    if (cur->_col == BLACK)
    {
      blackNum++;
    }
    return Check(cur->_left,blackNum,refBlackNum) && Check(cur->_right,blackNum,refBlackNum);
  }
  bool IsBalance()
  {
    if (_root && _root->_col == RED)
      return false;
    //统计一条路径的黑色节点数
    int refBlackNum = 0;
    Node* cur = _root;
    while (cur)
    {
      if (cur->_col == BLACK)
      {
        refBlackNum++;
      }
      cur = cur->_left;
    }
    return Check(_root,0,refBlackNum);
  }
  size_t Size()
  {
    return _Size(_root);
  }
  size_t _Size(Node* root)
  {
    if (root == NULL)
      return 0;
    return _Size(root->_left)
      + _Size(root->_right) + 1;
  }
  Node* Find(const K& key)
  {
    Node* cur = _root;
    while (cur)
    {
      if (cur->_kv.first < key)
      {
        cur = cur->_right;
      }
      else if (cur->_kv.first > key)
      {
        cur = cur->_left;
      }
      else
      {
        return cur;
      }
    }
    return NULL;
  }
  int _Height(Node* root)
  {
    if (root == nullptr)
      return 0;
    int leftHeight = _Height(root->_left);
    int rightHeight = _Height(root->_right);
    return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
  }
  int Height()
  {
    return _Height(_root);
  }
private:
  Node* _root = nullptr;
};
void TestRBTree1()
{
  int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
  RBTree<int, int> t;
  for (auto e : a)
  {
    t.Insert(make_pair(e, e));
  }
  t.InOrder();
  cout << t.IsBalance() << endl;
}
//性能测试
void TestRBTree2()
{
  const int N = 1000000;
  vector<int> v;
  v.reserve(N);
  srand(time(0));
  for (size_t i = 0; i < N; i++)
  {
    v.push_back(rand() + i);
  }
  //测插入所需时间
  size_t begin2 = clock();
  RBTree<int, int> t;
  for (auto a : v)
  {
    t.Insert(make_pair(a, a));
  }
  size_t end2 = clock();
  
  cout << "Insert:" << end2 - begin2 << endl;
  cout << t.IsBalance() << endl;
  //测高度,数量
  cout << "height" << t.Height() << endl;
  cout << "Size:" << t.Size() << endl;
  //测查找时间
  size_t begin1 = clock();
  for (size_t i = 0; i < N; i++)
  {
    t.Find((rand() + i));
  }
  size_t end1 = clock();
  cout << "Find:" << end1 - begin1 << endl;
}
相关文章
|
27天前
|
存储 C++
【C++】红黑树
红黑树是一种自平衡二叉搜索树,通过节点颜色(红或黑)及特定规则维持平衡,确保操作效率。其性质包括:每个节点非红即黑,根节点必黑,红节点的子节点必黑,从任一节点到其每个叶子的所有路径含相同数量的黑节点。实现上,通过节点结构定义、基本操作(插入、删除、旋转等)、维护平衡性质等步骤完成。代码示例展示了节点定义、插入操作及旋转调整方法。
25 2
【C++】红黑树
|
4月前
|
关系型数据库 C++ 容器
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——红黑树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——红黑树
40 0
|
5月前
|
Java C++ Python
【C++】手撕红黑树
【C++】手撕红黑树
30 1
|
6月前
|
编译器 C++
C++模板进阶
C++模板进阶
27 1
|
6月前
|
存储 算法 程序员
【C++进阶】深入STL之 栈与队列:数据结构探索之旅
【C++进阶】深入STL之 栈与队列:数据结构探索之旅
62 4
|
6月前
|
算法 安全 编译器
【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想
【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想
61 1
|
6月前
|
存储 算法 程序员
【C++进阶】深入STL之vector:构建高效C++程序的基石
【C++进阶】深入STL之vector:构建高效C++程序的基石
70 1
|
6月前
|
编译器 C++
【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
44 1
|
6月前
|
关系型数据库 C++
【c++】红黑树
【c++】红黑树
26 0
|
6月前
|
存储 缓存 编译器
【C++进阶】深入STL之list:模拟实现深入理解List与迭代器
【C++进阶】深入STL之list:模拟实现深入理解List与迭代器
48 0