• 红黑树插入的实现


    前面我们已经实现了搜索二叉树树,知道了其查找规则;实现了AVL树,知道了他的旋转规则;今天这篇红黑树,是解决搜索二叉树退化成单链情况的另一种方法,以插入为重点。


    目录

    红黑树的性质

    性质

    实现

    结点构造

    插入实现 

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

    情况二: cur,p为红,g为黑,u不存在/u为黑--- g p cur在一条直线上

    情况三: cur,p为红,g为黑,u不存在/u为黑--- g p cur不在一条直线上

    代码实现


    红黑树的性质

    红黑树顾名思义,他的结点不是红的就是黑的;他通过结点的颜色和性质来保证了他的规则:

    红黑树确保没有一条路径会比其他路径长出俩倍

    性质

    1. 结点颜色不是红色就是黑色,根节点是黑色的


    2. 如果一个节点是红色的,则它的两个孩子结点是黑色的(没有连续红结点)


    3. 从任意一个结点开始的所有路径上,均包含相同数目的黑色结点


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

    实现

    结点构造

    AVL树中结点包含平衡因子,而红黑树包含的是结点的颜色。

    1. enum Colour
    2. {
    3. RED,
    4. BLACK,
    5. };
    6. template <class K,class V>
    7. struct RBTreeNode
    8. {
    9. RBTreeNode* _left;
    10. RBTreeNode* _right;
    11. RBTreeNode* _parent;
    12. pair _kv;
    13. Colour _col;
    14. RBTreeNode(const pair& kv)
    15. :_kv(kv)
    16. , _left(nullptr)
    17. , _right(nullptr)
    18. , _parent(nullptr)
    19. , _col(RED)
    20. {}
    21. };

    插入实现 

    接着是插入的实现,插入时要保证树的结构尽可能地受到地破坏最小,如果插入黑色结点的话,每条路径上地黑色结点数会不相同,影响整棵树,因此插入时应控制插入的结点为红色结点。

    插入主要以父亲和叔叔的颜色做为判断标志

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

    遇到上述这种情况时,此树可能是一颗完整的树,也可能只是一颗子树。

    a b c d 可能是子树也可能为空。

    如果父亲p和叔叔u都为红色,那么将 p u 都变成黑色,此时从g开始,左右两条路径的黑结点各自增加一个,每条路径黑结点相同,只是相对于这是一颗完整的树而言。

    如果这个树是子树,只保证了子树中每条路径黑结点相同,要保证整棵树每条路径下的黑色结点相同,我们要将祖父结点g变成红色。

    无论是完整的树还是子树,g变红不会影响每条路径的黑色结点相同的情况;

    再循环判断g是否为根:为根,将g的颜色变黑色,因为根节点必须是黑色;不为根,那么将g变为cur,继续往上查看是否有红色结点相连的情况

    情况二: cur,p为红,g为黑,u不存在/u为黑--- g p cur在一条直线上

    当出现左图情况时,cur 一定不是新插入的结点,因为要保证每条路径黑色结点相同,cur 原来一定为黑色,可能是cur的子树 a或者b 有连续的红色结点,颜色依次网上更新,将cur变成了红色节点。

    如果 u 不存在,为了保证每条路径黑色结点相同,g 的右子树只要g一个黑色结点,为了保证左子树也有g一个黑色结点,cur 本身只能为新增的红色结点。

    如上述情况,是左边连续红色结点,u 是黑色,为维护红黑树结构,这里以g为旋转点,进行右单旋,最后将p变为黑色,g变为红色即可。

    上图是左单边为连续红色,遇到右单边为连续红色,对g进行左单旋即可,最后将p变为黑色,g变为红色。

    情况三: cur,p为红,g为黑,u不存在/u为黑--- g p cur不在一条直线上

    当遇到最左边这样的情况时,需要进行两次旋转,先以p为旋转点,进行左单旋;最后以g为旋转点,进行右单旋,最后将cur变为黑色,g变为红色即可;

    如果p在g的右边,cur在p的左边,则先以p为旋转点进行右单旋,再以g为旋转点进行左单旋即可。

    代码实现

    1. template <class K,class V>
    2. class RBTree
    3. {
    4. typedef RBTreeNode Node;
    5. public:
    6. bool Insert(const pair& kv)
    7. {
    8. //1. 按照搜索树的规则插入
    9. //2. 看是否违反平衡规则,违反则旋转
    10. //3. 红黑树根节点是黑色
    11. //空树时
    12. if (_root == nullptr)
    13. {
    14. _root = new Node(kv);
    15. _root->_col = BLACK;
    16. return true;
    17. }
    18. //找到插入位置
    19. Node* parent = nullptr;
    20. Node* cur = _root;
    21. while (cur)
    22. {
    23. if (cur->_kv.first < kv.first)
    24. {
    25. parent = cur;
    26. cur = cur->_right;
    27. }
    28. else if (cur->_kv.first > kv.first)
    29. {
    30. parent = cur;
    31. cur = cur->_left;
    32. }
    33. else
    34. {
    35. return false;
    36. }
    37. }
    38. //插入红色结点
    39. cur = new Node(kv);
    40. cur->_col = RED;
    41. if (parent->_kv.first < kv.first)
    42. {
    43. parent->_right = cur;
    44. }
    45. else
    46. {
    47. parent->_left = cur;
    48. }
    49. cur->_parent = parent; //注意结点的更新
    50. //存在连续红色结点
    51. while (parent &&parent->_col == RED)
    52. {
    53. Node* grandfather = parent->_parent;
    54. if (grandfather->_left == parent) //找叔叔
    55. {
    56. Node* uncle = grandfather->_right; //叔叔在右边的前提下
    57. //情况1---叔叔颜色为红色
    58. if (uncle && uncle->_col == RED)
    59. {
    60. parent->_col = uncle->_col = BLACK;
    61. grandfather->_col = RED;
    62. //可能是子树--继续向上处理
    63. cur = grandfather;
    64. parent = cur->_parent;
    65. }
    66. else //叔叔存在且为黑或者叔叔不存在
    67. {
    68. //情况二,g p cur在一条直线上
    69. if (cur == parent->_left)
    70. {
    71. RotateR(grandfather);
    72. parent->_col = BLACK;
    73. grandfather->_col = RED;
    74. }
    75. //情况三: g p cur不在一条直线上,双旋--先左单旋,再右单旋
    76. else
    77. {
    78. RotateL(parent);
    79. RotateR(grandfather);
    80. cur->_col = BLACK;
    81. grandfather->_col = RED;
    82. }
    83. break;
    84. }
    85. }
    86. else //叔叔在左边的前提下
    87. {
    88. Node* uncle = grandfather->_left;
    89. //情况1---叔叔颜色为红色
    90. if (uncle && uncle->_col == RED)
    91. {
    92. parent->_col = uncle->_col = BLACK;
    93. grandfather->_col = RED;
    94. //可能是子树--继续向上处理
    95. cur = grandfather;
    96. parent = cur->_parent;
    97. }
    98. //情况二,g p cur都在右侧
    99. else
    100. {
    101. // g
    102. // p
    103. // c
    104. if (cur == parent->_right)
    105. {
    106. RotateL(grandfather);
    107. parent->_col = BLACK;
    108. grandfather->_col = RED;
    109. }
    110. //情况三--不在一条线上,需要双旋
    111. else
    112. {
    113. // g
    114. // p
    115. // c
    116. RotateR(parent);
    117. RotateL(grandfather);
    118. cur->_col = BLACK;
    119. grandfather->_col = RED;
    120. }
    121. break;
    122. }
    123. }
    124. }
    125. _root->_col = BLACK; //最后的时候根变为黑色即可·、
    126. return true;
    127. }
    128. private:
    129. //右单旋
    130. void RotateR(Node* parent)
    131. {
    132. Node* subL = parent->_left;
    133. Node* subLR = subL->_right;
    134. parent->_left = subLR;
    135. if (subLR)
    136. subLR->_parent = parent;
    137. Node* ppNode = parent->_parent;
    138. subL->_right = parent;
    139. parent->_parent = subL;
    140. if (parent == _root)
    141. {
    142. _root = subL;
    143. _root->_parent = nullptr;
    144. }
    145. else
    146. {
    147. if (parent == ppNode->_left)
    148. {
    149. ppNode->_left = subL;
    150. }
    151. else
    152. {
    153. ppNode->_right = subL;
    154. }
    155. subL->_parent = ppNode;
    156. }
    157. }
    158. //左单旋
    159. void RotateL(Node* parent)
    160. {
    161. Node* subR = parent->_right;
    162. Node* subRL = subR->_left;
    163. parent->_right = subRL;
    164. if (subRL)
    165. subRL->_parent = parent;
    166. Node* ppNode = parent->_parent;
    167. subR->_left = parent;
    168. parent->_parent = subR;
    169. if (parent == _root)
    170. {
    171. _root = subR;
    172. _root->_parent = nullptr;
    173. }
    174. else
    175. {
    176. if (parent == ppNode->_left)
    177. {
    178. ppNode->_left = subR;
    179. }
    180. else
    181. {
    182. ppNode->_right = subR;
    183. }
    184. subR->_parent = ppNode;
    185. }
    186. }
    187. Node* _root = nullptr;
    188. };

  • 相关阅读:
    基础 | 安全 - [加密]
    推荐一个微软反向代理组件+NetCore开发的API网关
    cpp学习笔记:STL deque容器
    C#面向对象程序设计课程实验一:实验名称:C#语言基础、程序流程控制
    Bigemap如何添加谷歌历史影像
    关于 OPENSSL_Uplink(XX……XX,08): no OPENSSL_Applink 处理
    国产猫粮高端化难题不少,网易天成拿什么出众?
    交换机堆叠+链路聚合+浮动静态路由
    input限制两位小数
    21天经典算法之折半插入排序
  • 原文地址:https://blog.csdn.net/weixin_53316121/article/details/126419676