当前位置:   article > 正文

C++数据结构 —— 红黑树_c++红黑树

c++红黑树

目录

1.红黑树概念

 2.红黑树节点的定义

3.红黑树的插入操作

4.红黑树的调整动作

4.1调整动作1

4.2调整动作2

4.3调整动作3

4.4插入算法的完整代码

4.5验证红黑树

4.6完整代码

1.红黑树概念

AVL树一样,红黑树也是map、set等关联式容器的底层结构。但红黑树是现代主流的底层结构,STL使用的便是红黑树。其原因在于:AVL树保持平衡的方法太过于绝对(必须保证每个节点的左右子树的高度差不超过1),而红黑树的性质保证了其具有一定的"柔韧性"以及可观的效率。

红黑树的本质也是一颗二叉搜索树,但在原有的基础上做了平衡处理。红黑树的每个节点都会增加一个存储位,用来表示节点的颜色,可以是红色也可以是黑色

 红黑树具有以下几点性质:

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

        2.根节点必须是黑色

        3.如果一个节点是红色的,它的父节点或者两个子节点都必须为黑色

        4.任何一条路径上的黑色节点数量都是相等的

        5.空节点也认为是一颗红黑树,它也是假象的黑色

满足上面的条件之后,就能达到具有一定"柔韧性"的平衡:红黑树的最长路径的节点个数不会超过最短路径节点个数的两倍。其原因如下图:

 2.红黑树节点的定义

  1. enum Corlor //枚举颜色
  2. {
  3. RED,BLACK
  4. };
  5. template <class K> //为了方便,使用K模型
  6. struct RBTreeNode
  7. {
  8. RBTreeNode<K>* _left;
  9. RBTreeNode<K>* _right;
  10. RBTreeNode<K>* _parent;
  11. K _key;
  12. Corlor _cor;
  13. RBTreeNode(const K& key)
  14. :_left(nullptr),_right(nullptr),_parent(nullptr),
  15. _key(key),_cor(RED)
  16. {}
  17. };

3.红黑树的插入操作

红黑树的插入方式与二叉搜索树一样,需要注意的是,插入的新节点的颜色可以红色,也可以是黑色,但是我们选择红色。因为如果新插入的节点是黑色,那么势必会破坏性质4,当红黑树有多条路径时,维护的成本会非常大。如果新插入的节点是红色,可以赌它的父节点是黑色,如果违反了性质3,我们仅需做一些微调即可。

  1. template <class K>
  2. class RBTree
  3. {
  4. typedef RBTreeNode<K> Node;
  5. public:
  6. bool insert(const K& key)
  7. {
  8. if (_root == nullptr)
  9. {
  10. _root = new Node(key);
  11. _root->_cor = BLACK; //根节点的颜色必须是黑色
  12. return true;
  13. }
  14. Node* parent = nullptr;
  15. Node* cur = _root;
  16. while (cur)
  17. {
  18. if (key < cur->_key)
  19. {
  20. parent = cur;
  21. cur = cur->_left;
  22. }
  23. else if (key > cur->_key)
  24. {
  25. parent = cur;
  26. cur = cur->_right;
  27. }
  28. else
  29. {
  30. return false;
  31. }
  32. }
  33. cur = new Node(key);
  34. cur->_cor = RED; //新插入的节点颜色为红色
  35. if (key < parent->_key)
  36. {
  37. parent->_left = cur;
  38. cur->_parent = parent;
  39. }
  40. else if (key > parent->_key)
  41. {
  42. parent->_right = cur;
  43. cur->_parent = parent;
  44. }
  45. // 此时如果新插入的节点破坏了红黑树的性质,就必须做一些微调
  46. while (parent && parent->_cor == RED)
  47. {
  48. // 调整动作
  49. }
  50. }
  51. private:
  52. Node* _root = nullptr;
  53. };

4.红黑树的调整动作

红黑树的调整分为三种动作, 需要调整的情况分别为:

        1.cur为红色,parent为红色,gradfather(parent的父节点)为红色,并且uncle(parent的兄弟节点)节点存在且为红色

        2.cur为红色,parent为红色,grandfather为黑色(这三个节点同在一侧),uncle存在且为黑色或uncle不存在

        3.cur为红色,parent为红色,grandfather为黑色(这三个节点不在同一侧),uncle存在且为黑色或uncle不存在

4.1调整动作1

针对情况1, 可以做出如下调整动作:

        1.让parent和uncle的颜色变黑

        2.让grandfather的颜色变红

        3.如果grandfather为根节点,则让其颜色变黑,结束调整;如果grandfather为某一子树,那么让cur = grandfather,parent = cur->_parent继续向上调整

4.2调整动作2

对于情况2,它是由情况1变过来的:

以这种情况为例,有AVL树的基础可以很明显的看出要以grandfather为轴进行一个右单旋;那么对应的,如果grandfather、parent、cur在右侧连成一线,就要使用左单旋。旋转完成之后,需要将parent置黑色,grandfather置红色

4.3调整动作3

对于情况3,它是由情况1变过来的:

 有过AVL树基础,可以明显的看出,此时要以parent为轴做一个左单旋;再以grandfather为轴做一个右单旋。事实上,当以parent为轴做一个左单旋时候,就立马回到了情况2。旋转完成之后,需要将cur置黑色,grandfather置红色

 综上,情况1可以转化为情况2或情况3,如果没有转化,证明调整结束;针对情况2,调整完毕即可结束;针对情况3,调整完毕即可结束。

4.4插入算法的完整代码

在调整红黑树时会使用旋转算法,AVL树中已经介绍过了。大家仅需把AVL树中的旋转算法的有关平衡因子的部分删除掉即可。

  1. bool insert(const K& key)
  2. {
  3. if (_root == nullptr)
  4. {
  5. _root = new Node(key);
  6. _root->_cor = BLACK; //根节点的颜色必须是黑色
  7. return true;
  8. }
  9. Node* parent = nullptr;
  10. Node* cur = _root;
  11. while (cur)
  12. {
  13. if (key < cur->_key)
  14. {
  15. parent = cur;
  16. cur = cur->_left;
  17. }
  18. else if (key > cur->_key)
  19. {
  20. parent = cur;
  21. cur = cur->_right;
  22. }
  23. else
  24. {
  25. return false;
  26. }
  27. }
  28. cur = new Node(key);
  29. cur->_cor = RED; //新插入的节点颜色为红色
  30. if (key < parent->_key)
  31. {
  32. parent->_left = cur;
  33. cur->_parent = parent;
  34. }
  35. else if (key > parent->_key)
  36. {
  37. parent->_right = cur;
  38. cur->_parent = parent;
  39. }
  40. // 此时如果新插入的节点破坏了红黑树的性质,就必须做一些微调
  41. while (parent && parent->_cor == RED)
  42. {
  43. // 调整动作
  44. Node* grandfather = parent->_parent;
  45. if (grandfather->_left == parent)
  46. {
  47. Node* uncle = grandfather->_right;
  48. if (uncle && uncle->_cor == RED)
  49. {
  50. // 情况1
  51. parent->_cor = uncle->_cor = BLACK;
  52. grandfather->_cor = RED;
  53. cur = grandfather;
  54. parent = cur->_parent;
  55. }
  56. else // unle不存在或uncle存在且为黑
  57. {
  58. if (parent->_left == cur)
  59. {
  60. // 情况2
  61. RotateR(grandfather);
  62. parent->_cor = BLACK;
  63. grandfather->_cor = RED;
  64. }
  65. else if(parent->_right == cur)
  66. {
  67. // 情况3
  68. RotateL(parent);
  69. RotateR(grandfather);
  70. cur->_cor = BLACK;
  71. grandfather->_cor = RED;
  72. }
  73. break; //关键
  74. }
  75. }
  76. else if (grandfather->_right == parent) //镜像即可
  77. {
  78. Node* uncle = grandfather->_left;
  79. if (uncle && uncle->_cor == RED)
  80. {
  81. parent->_cor = uncle->_cor = BLACK;
  82. grandfather->_cor = RED;
  83. cur = grandfather;
  84. parent = cur->_parent;
  85. }
  86. else
  87. {
  88. if (parent->_right == cur)
  89. {
  90. // 情况2
  91. RotateL(grandfather);
  92. parent->_cor = BLACK;
  93. grandfather->_cor = RED;
  94. }
  95. else if (parent->_left == cur)
  96. {
  97. // 情况3
  98. RotateR(parent);
  99. RotateL(grandfather);
  100. cur->_cor = BLACK;
  101. grandfather->_cor = RED;
  102. }
  103. break; //关键
  104. }
  105. }
  106. }
  107. _root->_cor = BLACK; //确保根节点颜色为黑色
  108. return true;
  109. }

4.5验证红黑树

逐一验证红黑树的各个性质即可。

  1. bool isRBTree()
  2. {
  3. if (_root == nullptr)
  4. {
  5. return true; //空树也可以是红黑树
  6. }
  7. if (_root->_cor != BLACK)
  8. {
  9. cout << "根节点不为黑色!" << endl;
  10. return false; //根节点颜色不为黑色
  11. }
  12. // 计算任意一条路径的黑色节点数量
  13. int black_cnt = 0;
  14. Node* cur = _root;
  15. while (cur)
  16. {
  17. if (cur->_cor == BLACK)
  18. {
  19. ++black_cnt;
  20. }
  21. cur = cur->_left;
  22. }
  23. return __isRBTree(_root, 0, black_cnt);
  24. }
  25. bool __isRBTree(const Node* root, int cnt, int key)
  26. {
  27. if (root == nullptr)
  28. {
  29. if (cnt != key)
  30. {
  31. cout << "每条路径的黑色节点数量不相等!" << endl;
  32. return false;
  33. }
  34. return true;
  35. }
  36. if (root->_cor == BLACK)
  37. {
  38. ++cnt;
  39. }
  40. // 顺便判断是否存在连续的红节点
  41. Node* parent = root->_parent;
  42. if (parent && parent->_cor == RED && root->_cor == RED)
  43. {
  44. cout << "存在连续的红节点!" << endl;
  45. return false;
  46. }
  47. return __isRBTree(root->_left, cnt, key) && __isRBTree(root->_right, cnt, key);
  48. }

4.6完整代码

红黑树

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/55272
推荐阅读
相关标签
  

闽ICP备14008679号