• 深入理解 红黑树【满足红黑树5条规则】


    目录

    一.红黑树

    1.概念

    2.性质

    二.分部实现红黑树

    1.树节点

    2.红黑树成员

    3.插入(重点)

    情况一:叔叔节点存在且为红。

     情况二:叔叔不存在 or 叔叔存在且为黑

    ① 单旋情况

    ② 双旋情况

    注意:

    4.判断该树是否为红黑树

    三.红黑树实现代码


    AVLTree-》深入理解AVLTree【旋转控制平衡(单旋、双旋)】_糖果雨滴a的博客-CSDN博客

     使用红黑树封装实现的map和set:C++ 关联式容器map+set_糖果雨滴a的博客-CSDN博客

     

    前言:在学习红黑树之前,我们最好先学习一下AVLTree,并且这两个平衡二叉搜索树的难度差不多,学过了AVLTree之后,红黑树就会更加轻松一些。红黑树只是比较抽象一些,在调整方面较AVLTree要简单一些。

    一.红黑树

    1.概念

            红黑树,是一种二叉搜索树,但在每个结点上增加一个存储为表示结点的颜色,可以是Red或Black。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其它路径长两倍以上,因而是接近平衡的。

    2.性质

    ① 每个结点不是红色就是黑色

    ② 根节点是黑色的

    ③ 如果一个结点是红色的,则它的两个孩子结点是黑色的

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

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

    二.分部实现红黑树

    1.树节点

            实现一个含义3叉链的二叉树,并且包括pair类型的kv,以及枚举RED, BLACK颜色控制。

    1. enum Color
    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. Color _col;
    14. RBTreeNode(const pair& kv)
    15. : _kv(kv)
    16. , _left(nullptr)
    17. , _right(nullptr)
    18. , _parent(nullptr)
    19. , _col(RED)
    20. {}
    21. };

    2.红黑树成员

    1. template<class K, class V>
    2. class RBTree
    3. {
    4. typedef RBTreeNode Node;
    5. private:
    6. Node* _root = nullptr;
    7. }

    3.插入(重点)

            先简单看一下插入的代码: 

    1. bool Insert(const pair& kv)
    2. {
    3. // 1.搜索树的规则插入
    4. // 2.看是否违反平衡规则,如果违反就需要处理:旋转
    5. if (_root == nullptr)
    6. {
    7. _root = new Node(kv);
    8. _root->_col = BLACK;
    9. return true;
    10. }
    11. Node* parent = nullptr;
    12. Node* cur = _root;
    13. while (cur)
    14. {
    15. if (cur->_kv.first < kv.first)
    16. {
    17. parent = cur;
    18. cur = cur->_right;
    19. }
    20. else if (cur->_kv.first > kv.first)
    21. {
    22. parent = cur;
    23. cur = cur->_left;
    24. }
    25. else
    26. {
    27. return false;
    28. }
    29. }
    30. cur = new Node(kv);
    31. cur->_col = RED;
    32. if (parent->_kv.first < kv.first)
    33. {
    34. parent->_right = cur;
    35. }
    36. else
    37. {
    38. parent->_left = cur;
    39. }
    40. cur->_parent = parent;
    41. // 存在连续红色节点
    42. while (parent && parent->_col == RED)
    43. {
    44. Node* grandfather = parent->_parent;
    45. assert(grandfather);
    46. if (grandfather->_left == parent)
    47. {
    48. Node* uncle = grandfather->_right;
    49. // 情况一:叔叔存在且为红
    50. if (uncle && uncle->_col == RED)
    51. {
    52. // 变色
    53. parent->_col = uncle->_col = BLACK;
    54. grandfather->_col = RED;
    55. // 继续往上处理
    56. cur = grandfather;
    57. parent = cur->_parent;
    58. }
    59. // 叔叔不存在 or 叔叔存在且为黑
    60. else
    61. {
    62. // 情况二:单旋(右单旋)
    63. if (cur == parent->_left)
    64. {
    65. // g
    66. // p
    67. // c
    68. RotateR(grandfather);
    69. parent->_col = BLACK;
    70. grandfather->_col = RED;
    71. }
    72. // 情况三:双旋(左右双旋)
    73. else
    74. {
    75. // g
    76. // p
    77. // c
    78. RotateL(parent);
    79. RotateR(grandfather);
    80. cur->_col = BLACK;
    81. grandfather->_col = RED;
    82. }
    83. break;
    84. }
    85. }
    86. // grandfather->_right == parent
    87. else
    88. {
    89. Node* uncle = grandfather->_left;
    90. // 情况一:叔叔存在且为红
    91. if (uncle && uncle->_col == RED)
    92. {
    93. // 变色
    94. parent->_col = uncle->_col = BLACK;
    95. grandfather->_col = RED;
    96. // 继续往上处理
    97. cur = grandfather;
    98. parent = cur->_parent;
    99. }
    100. // 叔叔不存在 or 叔叔存在且为黑
    101. else
    102. {
    103. // 情况二:单旋(左单旋)
    104. if (cur == parent->_right)
    105. {
    106. // g
    107. // p
    108. // c
    109. RotateL(grandfather);
    110. parent->_col = BLACK;
    111. grandfather->_col = RED;
    112. }
    113. // 情况三:双旋(右左双旋)
    114. else
    115. {
    116. // g
    117. // p
    118. // c
    119. RotateR(parent);
    120. RotateL(grandfather);
    121. cur->_col = BLACK;
    122. grandfather->_col = RED;
    123. }
    124. break;
    125. }
    126. }
    127. }
    128. _root->_col = BLACK;
    129. return true;
    130. }

            前面就是正常的找到插入的位置,然后插入节点。

            这里我们默认插入的节点的颜色是红色,如果默认是黑色一定违反规则四,而是红色就只是可能会违反规则三,相比较而言,插入节点颜色为红色的影响较小

            接下来就是红黑树的重点部分:①符合红黑树规则 ②保证该搜索二叉树相对平衡。

            因为我们插入的节点是红色,那么只有出现连续的红色节点(即插入节点为红色,其父节点也为红色时)才需要进行调整。而具体情况主要与叔叔节点有关。

            这里我们先定义一个祖父节点grandfather,因为出现这种情况时父节点为红色,而如果没有grandfather,就一定违反规则二(根节点为黑色),因此grandfather一定存在,为了防止出现问题,这里我们assert一下。

            接下来就需要分情况了,首先是parent在祖父节点的左还是右需要分情况,因为左和右的不同,我们在后面进行旋转控制平衡时的旋转方向是不同的,因此要区分开来。

            这里我们以parent在grandfather的左为例,首先定义一个叔叔节点(grandfather的另一个孩子节点),这个叔叔节点是红黑树的关键。我们要以叔叔进行分情况。

    情况一:叔叔节点存在且为红。

            这种情况我们只需要变色处理,但是在变色处理后,可能导致上面的节点也出现此情况,因此要继续向上处理。

            处理方式:变色处理。

            操作:将parent和uncle的颜色改为BLACK,让grandfather的颜色改为RED。(这里我们不讨论grandfather是根节点的情况,我们在最后强制让root节点变为黑色)

            我们处理的原则是尽量保证原来的黑色节点的数量不变进行插入,因此按上述改颜色后黑色节点的数量是不会发生变化的。

            下面我们看图实现:

    g为grandfather,p为parent,u为uncle。

            代码实现:

    1. // 情况一:叔叔存在且为红
    2. if (uncle && uncle->_col == RED)
    3. {
    4. // 变色
    5. parent->_col = uncle->_col = BLACK;
    6. grandfather->_col = RED;
    7. // 继续往上处理
    8. cur = grandfather;
    9. parent = cur->_parent;
    10. }

     情况二:叔叔不存在 or 叔叔存在且为黑

            情况二要再次进行划分,可以再分为两种情况,一种是进行单旋,另一种是进行双旋。

    ① 单旋情况

            grandfather,parent,cur在一条线上时发生单旋,这里我们是以parent在grandfather的左为例,因此这里cur是parent的左节点。

            处理方式:右单旋+变色处理

            操作:首先是右单旋,这里在AVLTree中进行了详细解释,然后再把parent的颜色变为BLACK,grandfather的颜色变为RED。

            

            代码实现:

    1. // 情况二:单旋(右单旋)
    2. if (cur == parent->_left)
    3. {
    4. // g
    5. // p
    6. // c
    7. RotateR(grandfather);
    8. parent->_col = BLACK;
    9. grandfather->_col = RED;
    10. }

    ② 双旋情况

            grandfather,parent,cur不在一条线上是发生双旋,这种情况说明cur是parent的右节点。

            处理方式:左右双旋+变色处理

            操作:先左单旋后右双旋,然后把cur的颜色变为BLACK,grangfather的颜色变为RED

            代码实现:

    1. // 情况三:双旋(左右双旋)
    2. else
    3. {
    4. // g
    5. // p
    6. // c
    7. RotateL(parent);
    8. RotateR(grandfather);
    9. cur->_col = BLACK;
    10. grandfather->_col = RED;
    11. }

    注意:

            上面我们是以parent在grandfather的左为例,如果parent在grandfather的右,那么情况一是相同的,而情况二中的单旋和双旋分别从右单旋变为左单旋,从左右双旋变为右左双旋。而变色处理是相同的

    4.判断该树是否为红黑树

            要想判断该树是否为红黑树,就要依次去判断该树是否满足这5个规则,同时也要满足没有一条路径会比其它路径长两倍以上。 

    1. bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount)
    2. {
    3. // 走到null之后,判断k和black是否相等
    4. if (nullptr == pRoot)
    5. {
    6. if (k != blackCount)
    7. {
    8. cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
    9. return false;
    10. }
    11. return true;
    12. }
    13. // 统计黑色节点的个数
    14. if (BLACK == pRoot->_col)
    15. {
    16. k++;
    17. }
    18. // 检测当前节点与其双亲是否都为红色
    19. if (RED == pRoot->_col && pRoot->_parent && pRoot->_parent->_col == RED)
    20. {
    21. cout << "违反性质三:存在连在一起的红色节点" << endl;
    22. return false;
    23. }
    24. return _IsValidRBTree(pRoot->_left, k, blackCount) &&
    25. _IsValidRBTree(pRoot->_right, k, blackCount);
    26. }
    27. bool IsBalanceTree()
    28. {
    29. // 检查红黑树几条规则
    30. Node* pRoot = _root;
    31. // 空树也是红黑树
    32. if (nullptr == pRoot)
    33. return true;
    34. // 检测根节点是否满足情况
    35. if (BLACK != pRoot->_col)
    36. {
    37. cout << "违反红黑树性质二:根节点必须为黑色" << endl;
    38. return false;
    39. }
    40. // 获取任意一条路径中黑色节点的个数 -- 比较基准值
    41. size_t blackCount = 0;
    42. Node* pCur = pRoot;
    43. while (pCur)
    44. {
    45. if (BLACK == pCur->_col)
    46. blackCount++;
    47. pCur = pCur->_left;
    48. }
    49. // 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
    50. size_t k = 0;
    51. return _IsValidRBTree(pRoot, k, blackCount);
    52. }

    三.红黑树实现代码

    1. #pragma once
    2. #include
    3. enum Color
    4. {
    5. RED,
    6. BLACK,
    7. };
    8. template<class K, class V>
    9. struct RBTreeNode
    10. {
    11. RBTreeNode* _left;
    12. RBTreeNode* _right;
    13. RBTreeNode* _parent;
    14. pair _kv;
    15. Color _col;
    16. RBTreeNode(const pair& kv)
    17. : _kv(kv)
    18. , _left(nullptr)
    19. , _right(nullptr)
    20. , _parent(nullptr)
    21. , _col(RED)
    22. {}
    23. };
    24. template<class K, class V>
    25. class RBTree
    26. {
    27. typedef RBTreeNode Node;
    28. public:
    29. bool Insert(const pair& kv)
    30. {
    31. // 1.搜索树的规则插入
    32. // 2.看是否违反平衡规则,如果违反就需要处理:旋转
    33. if (_root == nullptr)
    34. {
    35. _root = new Node(kv);
    36. _root->_col = BLACK;
    37. return true;
    38. }
    39. Node* parent = nullptr;
    40. Node* cur = _root;
    41. while (cur)
    42. {
    43. if (cur->_kv.first < kv.first)
    44. {
    45. parent = cur;
    46. cur = cur->_right;
    47. }
    48. else if (cur->_kv.first > kv.first)
    49. {
    50. parent = cur;
    51. cur = cur->_left;
    52. }
    53. else
    54. {
    55. return false;
    56. }
    57. }
    58. cur = new Node(kv);
    59. cur->_col = RED;
    60. if (parent->_kv.first < kv.first)
    61. {
    62. parent->_right = cur;
    63. }
    64. else
    65. {
    66. parent->_left = cur;
    67. }
    68. cur->_parent = parent;
    69. // 存在连续红色节点
    70. while (parent && parent->_col == RED)
    71. {
    72. Node* grandfather = parent->_parent;
    73. assert(grandfather);
    74. if (grandfather->_left == parent)
    75. {
    76. Node* uncle = grandfather->_right;
    77. // 情况一:叔叔存在且为红
    78. if (uncle && uncle->_col == RED)
    79. {
    80. // 变色
    81. parent->_col = uncle->_col = BLACK;
    82. grandfather->_col = RED;
    83. // 继续往上处理
    84. cur = grandfather;
    85. parent = cur->_parent;
    86. }
    87. // 叔叔不存在 or 叔叔存在且为黑
    88. else
    89. {
    90. // 情况二:单旋(右单旋)
    91. if (cur == parent->_left)
    92. {
    93. // g
    94. // p
    95. // c
    96. RotateR(grandfather);
    97. parent->_col = BLACK;
    98. grandfather->_col = RED;
    99. }
    100. // 情况三:双旋(左右双旋)
    101. else
    102. {
    103. // g
    104. // p
    105. // c
    106. RotateL(parent);
    107. RotateR(grandfather);
    108. cur->_col = BLACK;
    109. grandfather->_col = RED;
    110. }
    111. break;
    112. }
    113. }
    114. // grandfather->_right == parent
    115. else
    116. {
    117. Node* uncle = grandfather->_left;
    118. // 情况一:叔叔存在且为红
    119. if (uncle && uncle->_col == RED)
    120. {
    121. // 变色
    122. parent->_col = uncle->_col = BLACK;
    123. grandfather->_col = RED;
    124. // 继续往上处理
    125. cur = grandfather;
    126. parent = cur->_parent;
    127. }
    128. // 叔叔不存在 or 叔叔存在且为黑
    129. else
    130. {
    131. // 情况二:单旋(左单旋)
    132. if (cur == parent->_right)
    133. {
    134. // g
    135. // p
    136. // c
    137. RotateL(grandfather);
    138. parent->_col = BLACK;
    139. grandfather->_col = RED;
    140. }
    141. // 情况三:双旋(右左双旋)
    142. else
    143. {
    144. // g
    145. // p
    146. // c
    147. RotateR(parent);
    148. RotateL(grandfather);
    149. cur->_col = BLACK;
    150. grandfather->_col = RED;
    151. }
    152. break;
    153. }
    154. }
    155. }
    156. _root->_col = BLACK;
    157. return true;
    158. }
    159. private:
    160. void RotateL(Node* parent)
    161. {
    162. Node* subR = parent->_right;
    163. Node* subRL = subR->_left;
    164. parent->_right = subRL;
    165. if (subRL)
    166. subRL->_parent = parent;
    167. Node* ppNode = parent->_parent;
    168. subR->_left = parent;
    169. parent->_parent = subR;
    170. if (parent == _root)
    171. {
    172. _root = subR;
    173. _root->_parent = nullptr;
    174. }
    175. else
    176. {
    177. if (parent == ppNode->_left)
    178. {
    179. ppNode->_left = subR;
    180. }
    181. else
    182. {
    183. ppNode->_right = subR;
    184. }
    185. subR->_parent = ppNode;
    186. }
    187. }
    188. void RotateR(Node* parent)
    189. {
    190. Node* subL = parent->_left;
    191. Node* subLR = subL->_right;
    192. parent->_left = subLR;
    193. if (subLR)
    194. subLR->_parent = parent;
    195. Node* ppNode = parent->_parent;
    196. subL->_right = parent;
    197. parent->_parent = subL;
    198. if (parent == _root)
    199. {
    200. _root = subL;
    201. _root->_parent = nullptr;
    202. }
    203. else
    204. {
    205. if (ppNode->_left == parent)
    206. {
    207. ppNode->_left = subL;
    208. }
    209. else
    210. {
    211. ppNode->_right = subL;
    212. }
    213. subL->_parent = ppNode;
    214. }
    215. }
    216. bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount)
    217. {
    218. // 走到null之后,判断k和black是否相等
    219. if (nullptr == pRoot)
    220. {
    221. if (k != blackCount)
    222. {
    223. cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
    224. return false;
    225. }
    226. return true;
    227. }
    228. // 统计黑色节点的个数
    229. if (BLACK == pRoot->_col)
    230. {
    231. k++;
    232. }
    233. // 检测当前节点与其双亲是否都为红色
    234. if (RED == pRoot->_col && pRoot->_parent && pRoot->_parent->_col == RED)
    235. {
    236. cout << "违反性质三:存在连在一起的红色节点" << endl;
    237. return false;
    238. }
    239. return _IsValidRBTree(pRoot->_left, k, blackCount) &&
    240. _IsValidRBTree(pRoot->_right, k, blackCount);
    241. }
    242. public:
    243. bool IsBalanceTree()
    244. {
    245. // 检查红黑树几条规则
    246. Node* pRoot = _root;
    247. // 空树也是红黑树
    248. if (nullptr == pRoot)
    249. return true;
    250. // 检测根节点是否满足情况
    251. if (BLACK != pRoot->_col)
    252. {
    253. cout << "违反红黑树性质二:根节点必须为黑色" << endl;
    254. return false;
    255. }
    256. // 获取任意一条路径中黑色节点的个数 -- 比较基准值
    257. size_t blackCount = 0;
    258. Node* pCur = pRoot;
    259. while (pCur)
    260. {
    261. if (BLACK == pCur->_col)
    262. blackCount++;
    263. pCur = pCur->_left;
    264. }
    265. // 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
    266. size_t k = 0;
    267. return _IsValidRBTree(pRoot, k, blackCount);
    268. }
    269. private:
    270. Node* _root = nullptr;
    271. };
  • 相关阅读:
    I2C总线实现逻辑
    Linux 日志系统
    30万以上的qps高并发服务如何优化
    Flutter高仿微信-第22篇-支付-二维码收款(二维码)
    【AI大模型】赋能儿童安全:楼层与室内定位实践与未来发展
    基于Springboot+vue的甜品蛋糕销售商城网站 elementui
    Python的math.sqrt()和math.pow()的使用
    web网页制作与实现 html+css+javascript+jquery+bootstarp响应式美食网站设计与实现
    zookeeper集群部署安装
    C++之类型转换
  • 原文地址:https://blog.csdn.net/qq_60750110/article/details/126199868