当前位置:   article > 正文

【C++进阶学习】第八弹——红黑树的原理与实现——探讨树形结构存储的最优解

【C++进阶学习】第八弹——红黑树的原理与实现——探讨树形结构存储的最优解

二叉搜索树:【C++进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫-CSDN博客

AVL树:

​​​​​​【C++进阶学习】第七弹——AVL树——树形结构存储数据的经典模块-CSDN博客

前言:

在前面,我们已经学习了二叉搜索树和它的改进AVL树,今天,我们来学习另一种由二叉搜索树改进而来的树形结构——红黑树

目录

一、红黑树的概念

二、红黑树的性质

三、红黑树的节点结构

四、红黑树的操作

五、红黑树的实现代码

六、总结


一、红黑树的概念

红黑树是一种特殊的二叉树,它的每个节点都增加一个存储为表示颜色,要么是黑色,要么是白色。并且通过对每条路径上添加节点时节点的颜色限制,来确保每个路径上的黑色节点数量一致,且最长路径长度最多是最短路径长度的两倍,因此达到平衡。

二、红黑树的性质

红黑树有以下五个性质:

  1. 节点是红色或黑色:每个节点都有一个颜色属性,颜色可以是红色或黑色。

  2. 根节点是黑色:树的根节点必须是黑色。

  3. 红色节点的子节点是黑色:如果一个节点是红色,则它的两个子节点必须是黑色(即不能有两个连续的红色节点)。

  4. 每个节点到其每个叶子节点的路径都包含相同数量的黑色节点:从任何节点到其每个叶子节点的路径上,经过的黑色节点的数量必须相同。

  5. 叶子节点是黑色:红黑树的叶子节点(通常是指空节点)被视为黑色。

三、红黑树的节点结构

  1. template<class K, class V>
  2. struct BSTreeNode
  3. {
  4. BSTreeNode<K, V>* _left; //左子树
  5. BSTreeNode<K, V>* _right; //右子树
  6. BSTreeNode<K, V>* _parent; //父亲
  7. pair<K, V> _kv; //存放节点值的
  8. string _col; //颜色(通过这个可以直到左右子树存在情况)
  9. //构造函数
  10. BSTreeNode(const pair<K, V>& kv)
  11. :_left(nullptr)
  12. , _right(nullptr)
  13. , _parent(nullptr)
  14. , _kv(kv)
  15. , _col("RED") //默认颜色为红色
  16. {}
  17. };

红黑树的节点结构与二叉搜索树和AVL树差别不大,最大的差别就是加入了一个新的存储点——颜色

四、红黑树的操作

红黑树的基本操作包括插入、删除和查找。以下是这些操作的简要说明:

  1. 插入

    • 将新节点插入到树中,初始时将其标记为红色。
    • 可能会破坏红黑树的性质,需要通过旋转和重新着色来恢复性质。
  2. 删除

    • 删除节点后,可能会破坏红黑树的性质。
    • 需要进行调整,包括旋转和重新着色,以恢复红黑树的性质。
  3. 查找

    • 查找操作与普通的二叉搜索树相同,通过比较节点值来决定向左或向右子树查找。

在这几个操作中,插入是红黑树的关键,因为这是构造红黑树的关键,怎样插入才能保证红黑树的节点颜色、路径长度符合规定,下面我们就来重点讲解一下红黑树的节点插入操作:

首先我们要先清楚一点,红黑树也是二叉搜索树,所以它肯定是符合二叉搜索树的性质的——左边子树值小于根,右边子树值大于根

问题的关键是如何保证插入后结构的颜色符合规定,要做到这一点,我们首先就先要摸清插入节点会遇到的所有情况,然后我们再分析如何解决这些情况:

(强调一下:一定要把前面AVL树中讲的左右旋先弄明白)

假设:cur表示当前节点,p表示父节点,g表示祖父节点,u为叔叔节点

情况一:cur为红,p为黑

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

解决方法:把p、u变成黑,g变为红,然后让g变为cur继续向上处理 

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

解决方法:把p变为黑,g变为红,然后进行左旋/右旋

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

解决方法:把p变为黑,g变为红,同时右旋/左旋

特殊情况:如果当cur的插入位置形似AVL树中的RL型或LR型时,要先进行旋转转换成上面几种情况

五、红黑树的实现代码

RBTree.h

  1. //红黑树
  2. #include<iostream>
  3. using namespace std;
  4. template<class K, class V>
  5. struct BSTreeNode
  6. {
  7. BSTreeNode<K, V>* _left; //左子树
  8. BSTreeNode<K, V>* _right; //右子树
  9. BSTreeNode<K, V>* _parent; //父亲
  10. pair<K, V> _kv; //存放节点值的
  11. string _col; //颜色(通过这个可以直到左右子树存在情况)
  12. //构造函数
  13. BSTreeNode(const pair<K, V>& kv)
  14. :_left(nullptr)
  15. , _right(nullptr)
  16. , _parent(nullptr)
  17. , _kv(kv)
  18. , _col("RED") //默认颜色为红色
  19. {}
  20. };
  21. template<class K, class V>
  22. class BSTree
  23. {
  24. typedef BSTreeNode<K, V> Node;
  25. public:
  26. bool Insert(const pair<K, V>& kv) //插入节点
  27. {
  28. //首先要先按照二叉搜索树的原则将新插入的节点先找好位置,
  29. //这一步与之前所讲差别不大
  30. if (_root == nullptr)
  31. {
  32. _root = new Node(kv);
  33. _root->_col = "BLACK";
  34. return true;
  35. }
  36. Node* parent = nullptr;
  37. Node* cur = _root;
  38. while (cur)
  39. {
  40. if (cur->_kv.first < kv.first)
  41. {
  42. parent = cur;
  43. cur = cur->_right;
  44. }
  45. else if (cur->_kv.first > kv.first)
  46. {
  47. parent = cur;
  48. cur = cur->_left;
  49. }
  50. else
  51. {
  52. return false;
  53. }
  54. }
  55. cur = new Node(kv);
  56. cur->_col = "RED"; //插入节点颜色为红色
  57. if (parent->_kv.first < kv.first)
  58. {
  59. parent->_right = cur;
  60. cur->_parent = parent;
  61. }
  62. else
  63. {
  64. parent->_left = cur;
  65. cur->_parent = parent;
  66. }
  67. //按搜索二叉树规则插入到指定位置后,再检验一下是否符合红黑树的规则进行相关调整
  68. while (parent && parent->_col == "RED") //当父节点存在且父节点为红时,进行相关调整
  69. {
  70. Node* grandfather = parent->_parent;
  71. if(parent==grandfather->_left) //p为g的左孩子时
  72. {
  73. // g
  74. // p
  75. Node* uncle = grandfather->_right;
  76. if (uncle && uncle->_col == "RED") //u存在且为红
  77. {
  78. // g
  79. // p u
  80. // cur
  81. parent->_col = uncle->_col = "BLACK";
  82. grandfather->_col = "RED";
  83. //继续往上更新
  84. cur = grandfather;
  85. parent = cur->_parent;
  86. }
  87. else //u不存在/u存在且为黑
  88. {
  89. // g
  90. // p
  91. // cur
  92. if (cur == parent->_left)
  93. {
  94. RotateR(grandfather);
  95. grandfather->_col = "RED";
  96. parent->_col = "BLACK";
  97. }
  98. else
  99. {
  100. //LR型
  101. RotateL(parent);
  102. RotateR(grandfather);
  103. cur->_col = "BLACK";
  104. grandfather->_col = "RED";
  105. }
  106. break;
  107. }
  108. }
  109. else //p为g的右孩子时,与上面的相反即可
  110. {
  111. Node* uncle = grandfather->_left;
  112. if (uncle && uncle->_col == "RED")
  113. {
  114. parent->_col = uncle->_col = "BLACK";
  115. grandfather->_col = "RED";
  116. //继续往上更新
  117. cur = grandfather;
  118. parent = cur->_parent;
  119. }
  120. else
  121. {
  122. if (cur == parent->_right)
  123. {
  124. // g
  125. // u p
  126. // c
  127. RotateL(grandfather);
  128. grandfather->_col = "RED";
  129. parent->_col = "BLACK";
  130. }
  131. else
  132. {
  133. // g
  134. // u p
  135. // c
  136. //RL型
  137. RotateR(parent);
  138. RotateL(grandfather);
  139. cur->_col = "BLACK";
  140. grandfather->_col = "RED";
  141. }
  142. break;
  143. }
  144. }
  145. }
  146. _root->_col = "Black";
  147. return true;
  148. }
  149. //进行旋转调整(旋转操作与AVL树中的完全一样,不明白的可以看我之前的文章)
  150. void RotateL(Node* parent) //左单旋
  151. {
  152. Node* subR = parent->_right;
  153. Node* subRL = subR->_left;
  154. Node* parentParent = parent->_parent;
  155. parent->_right = subRL;
  156. subR->_left = parent;
  157. if(subRL)
  158. subRL->_parent = parent;
  159. if (_root == parent)
  160. {
  161. _root = subR;
  162. subR->_parent = nullptr;
  163. }
  164. else
  165. {
  166. if (parentParent->_left == parent)
  167. {
  168. parentParent->_left = subR;
  169. }
  170. else
  171. {
  172. parentParent->_right = subR;
  173. }
  174. subR->_parent = parentParent;
  175. }
  176. parent->_parent = subR;
  177. }
  178. void RotateR(Node* parent) //右单旋
  179. {
  180. Node* subL = parent->_left;
  181. Node* subLR = subL->_right;
  182. Node* parentParent = parent->_parent;
  183. parent->_left = subLR;
  184. subL->_right = parent;
  185. if (subLR)
  186. {
  187. subLR->_parent = parent;
  188. }
  189. if (_root == parent)
  190. {
  191. _root = subL;
  192. subL->_parent = nullptr;
  193. }
  194. else
  195. {
  196. if (parentParent->_left == parent)
  197. {
  198. parentParent->_left = subL;
  199. }
  200. else
  201. {
  202. parentParent->_right = subL;
  203. }
  204. subL->_parent = parentParent;
  205. }
  206. parent->_parent = subL;
  207. }
  208. //中序打印
  209. void Inorder()
  210. {
  211. _Inorder(_root);
  212. cout << endl;
  213. }
  214. void _Inorder(Node* root)
  215. {
  216. if (root == nullptr)
  217. return;
  218. _Inorder(root->_left);
  219. cout << root->_kv.first << " ";
  220. _Inorder(root->_right);
  221. }
  222. //检查是否为BS树(检查两点)
  223. //一:是否有连续的红节点
  224. //二:各个路径上的黑色节点个数是否相等
  225. bool Check(Node* root,int blacknum,const int refVal)
  226. {
  227. if (root == nullptr)
  228. {
  229. if (blacknum != refVal)
  230. {
  231. cout << "存在黑色节点个数不相等的路径" << endl;
  232. return false;
  233. }
  234. return true;
  235. }
  236. if (root->_col == "RED"&&root->_parent->_col=="RED")
  237. {
  238. cout << "有连续的红色节点" << endl;
  239. return false;
  240. }
  241. if (root->_col == "BLACK")
  242. ++blacknum;
  243. return Check(root->_left, blacknum, refVal) //红黑树的左右子树也是红黑树
  244. && Check(root->_right, blacknum, refVal); //所以要用递归对左右子树也进行判断
  245. }
  246. bool IsBalance()
  247. {
  248. if (_root == nullptr)
  249. return true;
  250. if (_root->_col == "RED")
  251. return false;
  252. int refVal = 0; //参考值
  253. Node* cur = _root;
  254. while (cur)
  255. {
  256. if (cur->_col == "BLACK")
  257. {
  258. ++refVal;
  259. }
  260. cur = cur->_left;
  261. }
  262. int blacknum = 0; //根节点到当前节点黑色节点数量
  263. return Check(_root, blacknum,refVal);
  264. }
  265. private:
  266. Node* _root = nullptr;
  267. };

RBTree.c

  1. //红黑树
  2. int main()
  3. {
  4. int a[] = { 4,2,6,1,3,5,15,7,16,14 };
  5. //int a[] = { 790,760,969,270,31,424,377,24,702 };
  6. BSTree<int, int> t;
  7. for (auto e : a)
  8. {
  9. t.Insert(make_pair(e, e));
  10. }
  11. t.Inorder();
  12. cout << "是否是红黑树(1/0):"<<t.IsBalance() << endl;
  13. return 0;
  14. }

运行结果:

六、总结

以上就是红黑树的全部内容,红黑树因为其自平衡的特性,及通过节点颜色来操作其树形结构的特点,极大的提高了数据存储及处理的效率,需要我们好好掌握

感谢各位大佬观看,创作不易,还请各位大佬一键三连!!!

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

闽ICP备14008679号