• 数据结构——红黑树


    目录

    1.什么是红黑树?

    2.红黑树插入的模拟实现

    ①节点的定义

    ②插入

    1.uncle存在且为红

    2.uncle不存在或uncle存在且为黑

    3.红黑树的性能分析

    4.红黑树与AVL树的比较


    1.什么是红黑树?

    红黑树是一种特定类型的二叉树,用于组织数据。它是一种平衡二叉查找树(AVL树)的变体,每个结点都带有颜色属性(红色或黑色)。在红黑树中,从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。具体来说,红黑树满足以下性质:

    1. 每个结点要么是红色,要么是黑色。
    2. 根结点是黑色。
    3. 每个叶结点(NIL或空结点)是黑色的。
    4. 如果一个结点是红色的,则它的两个子结点都是黑色的。
    5. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。

    举个例子

    2.红黑树插入的模拟实现

    ①节点的定义

    1. // 在这里为了方便表示我们先将颜色枚举
    2. // 在红黑树中只有黑与红,即BLACK与RED
    3. enum Color
    4. {
    5. BLACK,
    6. RED
    7. };
    8. // 因为节点是公用的,因此设定为struct
    9. template<class K, class V>
    10. struct RBTreeNode
    11. {
    12. RBTreeNode(const pair& kv)
    13. :_kv(kv)
    14. , _left(nullptr)
    15. , _right(nullptr)
    16. , _parent(nullptr)
    17. // 根据红黑树的性质可以知道
    18. // 要插入的新节点不应该是黑色的,而应该是红色的
    19. // 如果是黑色的那么就会影响它当前路径的黑色节点总数
    20. , _color(RED)
    21. {}
    22. pair _kv;
    23. RBTreeNode* _left;
    24. RBTreeNode* _right;
    25. RBTreeNode* _parent;
    26. Color _color;
    27. };

    ②插入

    1. template<class K, class V>
    2. class RBTree
    3. {
    4. typedef RBTreeNode Node;
    5. bool Insert(const pair& kv)
    6. {
    7. if (_root == nullptr)
    8. {
    9. _root = new Node(kv);
    10. return true;
    11. }
    12. Node* cur = _root;
    13. Node* parent = nullptr;
    14. // 寻找要插入的位置
    15. while (cur)
    16. {
    17. if (kv.first < cur->_kv.first)
    18. {
    19. parent = cur;
    20. cur = cur->_left;
    21. }
    22. else if (kv.first > cur->_kv.first)
    23. {
    24. parent = cur;
    25. cur = cur->_right;
    26. }
    27. else
    28. {
    29. return false;
    30. }
    31. }
    32. // 到此处cur已经指向了应该插入的位置,
    33. // 然后判断应该插入到parent的哪边
    34. cur = new Node(kv);
    35. if (kv.first > parent->_kv.first)
    36. {
    37. parent->_right = cur;
    38. }
    39. else
    40. {
    41. parent->_left = cur;
    42. }
    43. cur->_parent = parent;
    44. // 插入完成后需要对红黑树进行调整
    45. // 若父节点是黑就无需调整
    46. // 而当父节点是红就需要进行调整
    47. // ...
    48. }
    49. private:
    50. Node* _root = nullptr;
    51. };

    以上面的红黑树为例,在parent为红的情况下,插入之后的情况可以将它们大致分为两种:1. uncle存在且为红;2.uncle不存在或uncle存在且为黑。让我们来分析一下,在这里我们依旧采取先具体后推广到一般的方法

    1.uncle存在且为红

    先举一个具体例子

     抽象图如下

    2.uncle不存在或uncle存在且为黑

    先举一个uncle不存在的具体例子

    再举一个uncle存在且为黑的具体例子 

    观察上述例子之后可以发现,这两种情况本质上可以当做同一操作,那就是先判断grandpa、parent与cur的关系,然后根据具体情况进行旋转,旋转之后进行变色,因此抽象图如下

    综合上面的所有情况,再加上一些细节我们可以得到如下插入代码

    1. bool Insert(const pair& kv)
    2. {
    3. if (_root == nullptr)
    4. {
    5. _root = new Node(kv);
    6. return true;
    7. }
    8. Node* cur = _root;
    9. Node* parent = nullptr;
    10. // 寻找要插入的位置
    11. while (cur)
    12. {
    13. if (kv.first < cur->_kv.first)
    14. {
    15. parent = cur;
    16. cur = cur->_left;
    17. }
    18. else if (kv.first > cur->_kv.first)
    19. {
    20. parent = cur;
    21. cur = cur->_right;
    22. }
    23. else
    24. {
    25. return false;
    26. }
    27. }
    28. // 到此处cur已经指向了应该插入的位置,
    29. // 然后判断应该插入到parent的哪边
    30. cur = new Node(kv);
    31. if (kv.first > parent->_kv.first)
    32. {
    33. parent->_right = cur;
    34. }
    35. else
    36. {
    37. parent->_left = cur;
    38. }
    39. cur->_parent = parent;
    40. // 插入完成后判断一下
    41. // 若父节点是黑就无需调整
    42. // 而当父节点是红就需要进行调整
    43. while (parent && parent->_color == RED)
    44. {
    45. Node* grandpa = parent->_parent;
    46. if (parent == grandpa->_left)
    47. {
    48. Node* uncle = parent->_right;
    49. if (uncle && uncle->_color == RED)
    50. {
    51. uncle->_color = parent->_color = BLACK;
    52. grandpa->_color = RED;
    53. cur = grandpa;
    54. parent = cur->_parent;
    55. }
    56. else
    57. {
    58. if (uncle == nullptr)
    59. {
    60. if (parent->_left == cur)
    61. {
    62. // grandpa
    63. // parent
    64. //cur
    65. RotateR(grandpa);
    66. grandpa->_color = RED;
    67. parent->_color = BLACK;
    68. }
    69. else
    70. {
    71. // grandpa
    72. // parent
    73. // cur
    74. RotateL(parent);
    75. RotateR(grandpa);
    76. cur->_color = BLACK;
    77. grandpa->_color = RED;
    78. }
    79. }
    80. else // uncle存在且为黑
    81. {
    82. if (parent->_left == cur)
    83. {
    84. // grandpa
    85. // parent
    86. //cur
    87. RotateR(grandpa);
    88. grandpa->_color = RED;
    89. parent->_color = BLACK;
    90. }
    91. else
    92. {
    93. // grandpa
    94. // parent
    95. // cur
    96. RotateL(parent);
    97. RotateR(grandpa);
    98. cur->_color = BLACK;
    99. grandpa->_color = RED;
    100. }
    101. }
    102. break;
    103. }
    104. }
    105. else // parent == grandpa->_right
    106. {
    107. Node* uncle = parent->_left;
    108. if (uncle && uncle->_color == RED)
    109. {
    110. uncle->_color = parent->_color = BLACK;
    111. grandpa->_color = RED;
    112. cur = grandpa;
    113. parent = cur->_parent;
    114. }
    115. else
    116. {
    117. if (uncle == nullptr)
    118. {
    119. if (parent->_left == cur)
    120. {
    121. //grandpa
    122. // parent
    123. //cur
    124. RotateR(parent);
    125. RotateL(grandpa);
    126. cur->_color = BLACK;
    127. grandpa->_color = RED;
    128. }
    129. else
    130. {
    131. //grandpa
    132. // parent
    133. // cur
    134. RotateL(grandpa);
    135. grandpa->_color = RED;
    136. parent->_color = BLACK;
    137. }
    138. }
    139. else // uncle存在且为黑
    140. {
    141. if (parent->_left == cur)
    142. {
    143. //grandpa
    144. // parent
    145. //cur
    146. RotateR(parent);
    147. RotateL(grandpa);
    148. cur->_color = BLACK;
    149. grandpa->_color = RED;
    150. }
    151. else
    152. {
    153. //grandpa
    154. // parent
    155. // cur
    156. RotateL(grandpa);
    157. grandpa->_color = RED;
    158. parent->_color = BLACK;
    159. }
    160. }
    161. break;
    162. }
    163. }
    164. }
    165. return true;
    166. }

    (注:左右旋参见数据结构——AVL树) 

    3.红黑树的性能分析

    红黑树可以在O(log n)时间内做查找、插入和删除,这里的n 是树中元素的数目。红黑树在最坏情况下的运行时间也是非常良好的,并且在实践中是高效的。红黑树的性能受限于它的平衡性,即每个叶子节点到根节点的路径上所包含的黑色节点的数量是相同的,这也被称为黑色完美平衡。保持这种平衡可以确保红黑树的性能稳定。总的来说,红黑树是一种高效的数据结构,适用于许多不同的情况。

    4.红黑树与AVL树的比较

    红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O($log_2 N$),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

  • 相关阅读:
    《C和指针》笔记27:递归
    GameOff2022参与有感
    LQ0149 排序【枚举】
    Web3 世界的名片
    工作流审批平台,可迁移,可快速开发审批单(源码)
    java+python+vue学习资源共享网站
    [安洵杯 2019]easy_serialize_php1
    自媒体短视频账号运营开始前的准备工作,做好这一步是成功的开始。
    关于mac下pycharm旧版本没删除的情况下新版本2023安装之后闪退
    一起Talk Android吧(第四百三十三回:Java8中的日期时间类)
  • 原文地址:https://blog.csdn.net/qq_73201597/article/details/132922831