经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
C++?STL容器详解之红黑树部分模拟实现
来源:jb51  时间:2021/12/8 8:38:10  对本文有异议

一、红黑树的概念

红黑树(Red Black Tree),是在计算机科学中用到的一种数据结构,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

二、红黑树的性质

1. 每个结点不是红色就是黑色;

2. 根节点是黑色的;

3. 如果一个节点是红色的,则它的两个孩子结点是黑色的;

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

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

满足上面的性质,红黑树就能保证其最长路径中节点个数不会超过最短路径节点个数的两倍。

三、红黑树节点的定义

  1. enum Colour //红黑树颜色枚举
  2. {
  3. RED,
  4. BLACK,
  5. };
  6. template<class K, class V>
  7. struct RBTreeNode //节点结构体
  8. {
  9. RBTreeNode<K, V>* _left; //左子树
  10. RBTreeNode<K, V>* _right; //右子树
  11. RBTreeNode<K, V>* _parent; //父节点
  12. pair<K, V> _kv;
  13. Colour _col;
  14. RBTreeNode(const pair<K, V>& kv) //构造函数
  15. : _left(nullptr)
  16. , _right(nullptr)
  17. , _parent(nullptr)
  18. , _kv(kv)
  19. , _col(RED)
  20. {}
  21. };

插入时默认为红色节点,因为红色可能会破坏规则3,黑色一定会破坏规则4,所以默认红色。

四、红黑树结构?

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 parent 域指向红黑树的根节点,left域指向红黑树中最小的节点,right域指向红黑树中最大的节点,如下:

五、 红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1. 按照二叉搜索的树规则插入新节点:

  1. pair<Node*, bool> Insert(const pair<K, V>& kv)
  2. {
  3. if (_root == nullptr)
  4. {
  5. _root = new Node(kv);
  6. _root->_col = BLACK;
  7. return make_pair(_root, true);
  8. }
  9. Node* parent = nullptr;
  10. Node* cur = _root;
  11. while (cur)
  12. {
  13. if (cur->_kv.first > kv.first)
  14. {
  15. parent = cur;
  16. cur = cur->_left;
  17. }
  18. else if (cur->_kv.first < kv.first)
  19. {
  20. parent = cur;
  21. cur = cur->_right;
  22. }
  23. else
  24. {
  25. return make_pair(cur, false);
  26. }
  27. }
  28. Node* newNode = new Node(kv);
  29. newNode->_col = RED;
  30. if (parent->_kv.first > kv.first)
  31. {
  32. parent->_left = newNode;
  33. newNode->_parent = parent;
  34. }
  35. else
  36. {
  37. parent->_right = newNode;
  38. newNode->_parent = parent;
  39. }
  40. cur = newNode;
  41. while (parent && parent->_col == RED) //违反规则三
  42. {
  43. }
  44. _root->_col = BLACK; //插入结束再次将根变为黑
  45. return make_pair(cur, true);
  46. }

2. 检测新节点插入后,红黑树的性质是否造到破坏

因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

情况一:cur为红,p为红,g为黑,u存在且为红

如果g是根节点,调整完成后,需要将g改为黑色,如果g是子树,g一定有父节点,且如果为红色呃,继续向上调整。

将p,u改为黑,g改为红,然后把g当成cur,继续向上调整 。

情况二: cur为红,p为红,g为黑,u不存在/u为黑

u的情况有两种:

1.如果u节点不存在,则cur一定是新插入节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路径黑色节点个数相同。

2.如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色的,现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改成红色。

p为g的左孩子,cur为p的左孩子,则进行右单旋转;

p为g的右孩子,cur为p的右孩子,则进行左单旋转。

p变黑,g变红。

情况三: cur为红,p为红,g为黑,u不存在/u为黑

需要进行双旋。

代码实现:

  1. while (parent && parent->_col == RED) //违反规则三
  2. {
  3. Node* grandfather = parent->_parent;
  4. if (parent == grandfather->_left) //左半边
  5. {
  6. Node* uncle = parent->_right;
  7. if (uncle && uncle->_col == red) //情况一
  8. {
  9. uncle->_col = BLACK;
  10. grandfather->_col = RED;
  11. parent->_col = BLACK;
  12. cur = grandfather; //迭代
  13. parent = cur->_parent;
  14. }
  15. else //情况2.3
  16. {
  17. if (cur == parent->_left) //单侧
  18. {
  19. RotateR(grandfather);
  20. grandfather->_col = RED;
  21. parent->_col = BLACK;
  22. }
  23. else //折
  24. {
  25. RotateL(parent);
  26. RotateR(grandfather);
  27. cur->_col = BLACK;
  28. grandfather->_col = RED;
  29. }
  30. break; //黑色数量无变化,不需要向上
  31. }
  32. }
  33. else // parent == grandfather->_right
  34. {
  35. Node* uncle = parent->_left;
  36. if (uncle && uncle->_col == red) //情况一
  37. {
  38. uncle->_col = BLACK;
  39. grandfather->_col = RED;
  40. parent->_col = BLACK;
  41. cur = grandfather; //迭代
  42. parent = cur->_parent;
  43. }
  44. else //情况2.3
  45. {
  46. if (cur == parent->_right) //单侧
  47. {
  48. RotateL(grandfather);
  49. grandfather->_col = RED;
  50. parent->_col = BLACK;
  51. }
  52. else //折
  53. {
  54. RotateR(parent);
  55. RotateL(grandfather);
  56. cur->_col = BLACK;
  57. grandfather->_col = RED;
  58. }
  59. break;
  60. }
  61. }
  62. }

六、代码

  1. #pragma once
  2. #include<iostream>
  3. #include<assert.h>
  4. using namespace std;
  5. enum Colour //红黑树颜色枚举
  6. {
  7. RED,
  8. BLACK,
  9. };
  10. template<class K, class V>
  11. struct RBTreeNode //节点结构体
  12. {
  13. RBTreeNode<K, V>* _left; //左子树
  14. RBTreeNode<K, V>* _right; //右子树
  15. RBTreeNode<K, V>* _parent; //父节点
  16. pair<K, V> _kv;
  17. Colour _col;
  18. RBTreeNode(const pair<K, V>& kv) //构造函数
  19. : _left(nullptr)
  20. , _right(nullptr)
  21. , _parent(nullptr)
  22. , _kv(kv)
  23. , _col(RED)
  24. {}
  25. };
  26. template<class K, class V>
  27. class RBTree
  28. {
  29. typedef RBTreeNode<K, V> Node;
  30. private:
  31. Node* _root;
  32. void RotateR(Node* parent)
  33. {
  34. Node* subL = parent->_left;
  35. Node* subLR = subL->_right;
  36. Node* parentP = parent->_parent;
  37. if (subLR) //左子树的右子树连接到父的右
  38. subLR->_parent = parent;
  39. parent->_left = subLR;
  40. subL->_right = parent;
  41. parent->_parent = subL;
  42. // 如果parent是根节点,根新指向根节点的指针
  43. if (parent == _root)
  44. {
  45. _root = subL;
  46. subL->_parent = nullptr;
  47. }
  48. else
  49. {
  50. // 如果parent是子树,可能是其双亲的左子树,也可能是右子树
  51. if (parentP->_left == parent)
  52. parentP->_left = subL;
  53. else
  54. parentP->_right = subL;
  55. subL->_parent = parentP;
  56. }
  57. }
  58. void RotateL(Node* parent)
  59. {
  60. Node* subR = parent->_right;
  61. Node* subRL = subR->_left;
  62. Node* parentP = parent->_parent;
  63. if (subRL)
  64. subRL->_parent = parent;
  65. parent->_right = subRL;
  66. subR->_left = parent;
  67. parent->_parent = subR;
  68. // 如果parent是根节点,根新指向根节点的指针
  69. if (parent == _root)
  70. {
  71. _root = subR;
  72. subR->_parent = nullptr;
  73. }
  74. else
  75. {
  76. // 如果parent是子树,可能是其双亲的左子树,也可能是右子树
  77. if (parentP->_left == parent)
  78. parentP->_left = subR;
  79. else
  80. parentP->_right = subR;
  81. subR->_parent = parentP;
  82. }
  83. }
  84. void _Destory(Node* root)
  85. {
  86. if (root == nullptr)
  87. {
  88. return;
  89. }
  90. _Destory(root->_left);
  91. _Destory(root->_right);
  92. delete root;
  93. }
  94. public:
  95. RBTree()
  96. :_root(nullptr)
  97. {}
  98. ~RBTree()
  99. {
  100. _Destory(_root);
  101. _root = nullptr;
  102. }
  103. Node* Find(const K& key)
  104. {
  105. Node* cur = _root;
  106. while (cur)
  107. {
  108. if (cur->_kv.first > key)
  109. {
  110. cur = cur->_left;
  111. }
  112. else if (cur->_kv < key)
  113. {
  114. cur = cur->_right;
  115. }
  116. else
  117. {
  118. return cur;
  119. }
  120. }
  121. return nullptr;
  122. }
  123. pair<Node*, bool> Insert(const pair<K, V>& kv)
  124. {
  125. if (_root == nullptr)
  126. {
  127. _root = new Node(kv);
  128. _root->_col = BLACK;
  129. return make_pair(_root, true);
  130. }
  131. Node* parent = nullptr;
  132. Node* cur = _root;
  133. while (cur)
  134. {
  135. if (cur->_kv.first > kv.first)
  136. {
  137. parent = cur;
  138. cur = cur->_left;
  139. }
  140. else if (cur->_kv.first < kv.first)
  141. {
  142. parent = cur;
  143. cur = cur->_right;
  144. }
  145. else
  146. {
  147. return make_pair(cur, false);
  148. }
  149. }
  150. Node* newNode = new Node(kv);
  151. newNode->_col = RED;
  152. if (parent->_kv.first > kv.first)
  153. {
  154. parent->_left = newNode;
  155. newNode->_parent = parent;
  156. }
  157. else
  158. {
  159. parent->_right = newNode;
  160. newNode->_parent = parent;
  161. }
  162. cur = newNode;
  163. while (parent && parent->_col == RED) //违反规则三
  164. {
  165. Node* grandfather = parent->_parent;
  166. if (parent == grandfather->_left) //左半边
  167. {
  168. Node* uncle = parent->_right;
  169. if (uncle && uncle->_col == red) //情况一
  170. {
  171. uncle->_col = BLACK;
  172. grandfather->_col = RED;
  173. parent->_col = BLACK;
  174. cur = grandfather; //迭代
  175. parent = cur->_parent;
  176. }
  177. else //情况2.3
  178. {
  179. if (cur == parent->_left) //单侧
  180. {
  181. RotateR(grandfather);
  182. grandfather->_col = RED;
  183. parent->_col = BLACK;
  184. }
  185. else //折
  186. {
  187. RotateL(parent);
  188. RotateR(grandfather);
  189. cur->_col = BLACK;
  190. grandfather->_col = RED;
  191. }
  192. break; //黑色数量无变化,不需要向上
  193. }
  194. }
  195. else // parent == grandfather->_right
  196. {
  197. Node* uncle = parent->_left;
  198. if (uncle && uncle->_col == red) //情况一
  199. {
  200. uncle->_col = BLACK;
  201. grandfather->_col = RED;
  202. parent->_col = BLACK;
  203. cur = grandfather; //迭代
  204. parent = cur->_parent;
  205. }
  206. else //情况2.3
  207. {
  208. if (cur == parent->_right) //单侧
  209. {
  210. RotateL(grandfather);
  211. grandfather->_col = RED;
  212. parent->_col = BLACK;
  213. }
  214. else //折
  215. {
  216. RotateR(parent);
  217. RotateL(grandfather);
  218. cur->_col = BLACK;
  219. grandfather->_col = RED;
  220. }
  221. break;
  222. }
  223. }
  224. }
  225. _root->_col = BLACK; //插入结束再次将根变为黑
  226. return make_pair(newNode, true);
  227. }
  228. };

总结

本文对红黑树进行了介绍,并对构造,插入,查找进行了模拟实现。

以上就是C++ STL容器详解之红黑树部分模拟实现的详细内容,更多关于C++ STL红黑树实现的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号