当前位置:   article > 正文

C++&&数据结构——红黑树

C++&&数据结构——红黑树

一,关于红黑树

红黑树也是一种平衡二叉搜索树,但在每个节点上增加一个存储位表示节点的颜色,颜色右两种,红与黑,因此也称为红黑树。

通过对任意一条从根到叶子的路径上各个节点着色方式的限制,红黑树可以确保没有一条路径会比其他路径长出两倍,因此是“近似平衡”。

下面就是一棵红黑树:

结合上面的图,我们可以了解下红黑树成立的各种条件:

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

根节点是黑色的

③如果一个节点是红色的,那么这个红色节点的两个孩子节点都是黑色的

④对于每个节点,从该节点到其所有后代叶结点的路径上,黑色节点的数量相同

⑤每个叶子节点都是黑色的(这里所说的叶子节点是上图中的空节点)

二,红黑树节点和结构定义

2.1 节点

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

2.2 结构

  1. template<class K, class V>
  2. class RBTree
  3. {
  4. typedef RBTreeNode<K, V> Node;
  5. public:
  6. //此处实现各种成员函数和接口
  7. private:
  8. Node* _root = nullptr;
  9. }

三,红黑树插入*

3.1 基本插入

基本插入也和之前的差不多,直接上代码:

  1. bool Insert(const pair<K, V>& kv)
  2. {
  3. if (_root == nullptr)
  4. {
  5. _root = new Node(kv);
  6. _root->_col = BLACK;
  7. return 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->_right;
  17. }
  18. else if (cur->_kv.first > kv.first)
  19. {
  20. parent = cur;
  21. cur = cur->_left;
  22. }
  23. else
  24. {
  25. return false;
  26. }
  27. }
  28. cur = new Node(kv);
  29. if (parent->_kv.first < kv.first)
  30. {
  31. parent->_right = cur;
  32. }
  33. else
  34. {
  35. parent->_left = cur;
  36. }
  37. cur->_parent = parent;
  38. //插入完成,开始改变颜色和调整旋转
  39. cur->_col = RED;//把新插入的节点都搞成红色,因为如果插入后改为黑色,一定违反规则四:每条路径的黑色节点数量相同
  40. //如果父亲是黑色节点或者插入前是空树,直接摊牌不玩了,不进入循环
  41. while (parent && parent->_col == RED)//父节点存在且父节点为红色
  42. {
  43. //这里的循环控制平衡,具体看下面的各种情况
  44. }
  45. }

按搜索树的性质插入之后就是要判断红黑树的性质是否遭到破坏。

新插入节点默认为红色,因此:如果双亲节点颜色是黑色,那么没有违反红黑树的任何性质,不需要调整,就是上面代码最后的循环直接跳过;但是如果新插入的节点的双亲为红色,就违反了上面的规则“红节点的孩子为黑”,也就是出现了连续的红节点,需要调整,就是进入上面的循环部分

此时就要分下面的几条情况来讨论,维持平衡的方法的关键就是看叔叔也就是父亲的兄弟节点的状态

3.2 情况一:uncle节点存在且为红

3.2.1 cur是新增节点

如下图(假设cur是新增):

uncle存在且为红时,我们直接将父节点和叔叔节点都变成黑,再将爷爷节点变为红,但是只这样做肯定不行,比如下图:

一次调整过后就会出现上面的情况,所以需要不断往上调整

3.2.2 cur不是新增节点

如下图:

如上图,cur本身是黑色,是树中原来的节点,因为子树有新增变成了红,所以对于cur节点有两种情况符合情况一:

①本身是新增,默认新增节点为红

②子树有新增,通过情况一的向上调整变成了红 

3.3 情况二+情况三:uncle不存在或者存在为黑

从情况一我们可以看出,cur可能是新节点也可能不是新节点,但最终结果都是变红。

如果uncle节点不存在,那么cur一定是新增,如果cur不是新增,最开始循环的条件为父节点存在且为红,又因为红节点的孩子为黑这条性质,cur必定为黑,但是由于uncle不存在,导致以爷爷节点为根的子树左右子树黑节点数量不一样。

所以如果uncle不存在,cur必定为新增,如下图:

而对于uncle节点存在且为黑时, 那么cur原来肯定是黑色的因为父节点是红的,右因为左右两边黑色节点数量要一致,cur的孩子也为红,这时候在cur孩子后面新增节点时,就变成了情况一,会不断向上调整颜色,也会把cur变成红色,如下图:

所以,情况二和情况三的最终情况都是cur为红,所以可以放在一起讨论

3.4 插入代码

  1. bool Insert(const pair<K, V>& kv)
  2. {
  3. if (_root == nullptr)
  4. {
  5. _root = new Node(kv);
  6. _root->_col = BLACK;
  7. return 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->_right;
  17. }
  18. else if (cur->_kv.first > kv.first)
  19. {
  20. parent = cur;
  21. cur = cur->_left;
  22. }
  23. else
  24. {
  25. return false;
  26. }
  27. }
  28. cur = new Node(kv);
  29. if (parent->_kv.first < kv.first)
  30. {
  31. parent->_right = cur;
  32. }
  33. else
  34. {
  35. parent->_left = cur;
  36. }
  37. cur->_parent = parent;
  38. //插入完成,开始改变颜色和调整旋转
  39. cur->_col = RED;//把新插入的节点都搞成红色,因为如果插入后改为黑色,一定违反规则四:每条路径的黑色节点数量相同
  40. //如果父亲是黑色节点或者插入前是空树,直接摊牌不玩了,不进入循环
  41. while (parent && parent->_col == RED)//父节点存在且父节点为红色
  42. {
  43. //找祖父
  44. Node* grandfather = parent->_parent;
  45. assert(grandfather);
  46. assert(grandfather->_col == BLACK);
  47. //红黑树的关键看叔叔,也就是父亲的兄弟
  48. if (parent == grandfather->_left)//父亲在爷爷的左边,叔叔分为几种情况分开讨论
  49. {
  50. Node* uncle = grandfather->_right;//父亲在左边,那么叔叔就在右边
  51. //情况一:uncle存在且为红
  52. if (uncle && uncle->_col == RED)
  53. {
  54. //父亲和叔叔变黑是为了替代祖父的黑,祖父变红是为了保持黑色节点数量不变
  55. parent->_col = uncle->_col = BLACK;//把父亲和叔叔变黑
  56. grandfather->_col = RED;//把祖父变红
  57. //继续往上处理 -- 将祖父当成新增节点,循环往上处理
  58. cur = grandfather;
  59. parent = cur->_parent;
  60. }
  61. //情况二+三,uncle不存在 + 存在且为黑
  62. else
  63. {
  64. //新增在左边:右单旋+变色
  65. // g p
  66. // p u --> c g
  67. //c u
  68. if (cur == parent->_left)
  69. {
  70. RotateR(grandfather);
  71. parent->_col = BLACK;
  72. grandfather->_col = RED;
  73. }
  74. //新增在右边:左单旋 + 右双旋+变色
  75. // g
  76. // p u -->
  77. // c
  78. else
  79. {
  80. RotateL(parent);
  81. RotateR(grandfather);
  82. cur->_col = BLACK;
  83. grandfather->_col = RED;
  84. }
  85. break;
  86. }
  87. }
  88. else//父亲在右边,叔叔可能在左边 (parent == grandfather->_right)
  89. {
  90. Node* uncle = grandfather->_left;
  91. //情况一
  92. if (uncle && uncle->_col == RED)
  93. {
  94. parent->_col = uncle->_col = BLACK;
  95. grandfather->_col = RED;
  96. //继续往上处理
  97. cur = grandfather;
  98. parent = cur->_parent;
  99. }
  100. else //情况二+三,uncle不存在 + 存在且为黑
  101. {
  102. //新增在右边:左单旋+变色
  103. // g
  104. // u p
  105. // c
  106. if (cur == parent->_right)
  107. {
  108. RotateL(grandfather);
  109. parent->_col = BLACK;
  110. grandfather->_col = RED;
  111. }
  112. //新增在左边:左右双旋+变色
  113. // g
  114. // u p
  115. // c
  116. else
  117. {
  118. RotateR(parent);
  119. RotateL(grandfather);
  120. cur->_col = BLACK;
  121. grandfather->_col = RED;
  122. }
  123. break;
  124. }
  125. }
  126. }
  127. //循环结束
  128. _root->_col = BLACK;
  129. return true;
  130. }

四,其他接口实现

4.1 左旋转函数

  1. //左旋转函数
  2. void RotateL(Node* parent)
  3. {
  4. Node* subR = parent->_right;
  5. Node* subRL = subR->_left;
  6. parent->_right = subRL;
  7. //subRL可能是空
  8. if (subRL)
  9. {
  10. subRL->_parent = parent;
  11. }
  12. //记录一下要旋转的parent节点的_parent,用于当parent是子树根时的调整
  13. Node* ppNode = parent->_parent;
  14. subR->_left = parent;
  15. parent->_parent = subR;
  16. //parent是整棵树的根
  17. if (_root == parent)
  18. {
  19. _root = subR;
  20. subR->_parent = nullptr;
  21. }
  22. else//parent是子树的根
  23. {
  24. if (ppNode->_left == parent)
  25. {
  26. ppNode->_left = subR;
  27. }
  28. else
  29. {
  30. ppNode->_right = subR;
  31. }
  32. subR->_parent = ppNode;
  33. }
  34. }

4.2 右旋转函数

  1. //右旋转函数
  2. void RotateR(Node* parent)
  3. {
  4. Node* subL = parent->_left;
  5. Node* subLR = subL->_right;
  6. parent->_left = subLR;
  7. //subLR可能是空
  8. if (subLR)
  9. {
  10. subLR->_parent = parent;
  11. }
  12. //记录一下要旋转的parent节点的_parent,用于当parent是子树根时的调整
  13. Node* ppNode = parent->_parent;
  14. subL->_right = parent;
  15. parent->_parent = subL;
  16. //parent是整颗树的根
  17. if (_root == parent)
  18. {
  19. _root = subL;
  20. subL->_parent = nullptr;
  21. }
  22. else
  23. {
  24. if (ppNode->_left == parent)
  25. {
  26. ppNode->_left = subL;
  27. }
  28. else
  29. {
  30. ppNode->_right = subL;
  31. }
  32. subL->_parent = ppNode;
  33. }
  34. }

4.3 检查函数

  1. public:
  2. bool IsBalance() //根据规则来检查
  3. {
  4. if (_root && _root->_col == RED)
  5. {
  6. cout << "根节点不是黑色" << endl;
  7. return false;
  8. }
  9. //定义基准值,用来判断每条路径的黑色节点数量是否相同
  10. int benchmark = 0;
  11. Node* cur = _root;
  12. while (cur)
  13. {
  14. if (cur->_col == BLACK)
  15. {
  16. ++benchmark;
  17. }
  18. cur = cur->_left;
  19. }
  20. //检查连续红色节点
  21. return _Check(_root,0,benchmark);
  22. }
  23. private:
  24. bool _Check(Node* root,int blackNum,int benchmark)
  25. {
  26. if (root == nullptr)
  27. {
  28. if (benchmark != blackNum)
  29. {
  30. cout << "某条路径黑色节点数量不相等";
  31. return false;
  32. }
  33. return true;
  34. }
  35. if (root->_col == BLACK)
  36. {
  37. ++blackNum;
  38. }
  39. if (root->_col == RED && root->_parent && root->_parent->_col == RED)
  40. {
  41. cout << "存在连续红节点" << endl;
  42. return false;
  43. }
  44. return _Check(root->_left,blackNum,benchmark) && _Check(root->_right,blackNum,benchmark);
  45. }

 4.4 打印

  1. void InOrder()
  2. {
  3. _InOrder(_root);
  4. cout << endl;
  5. }
  6. private:
  7. void _InOrder(Node* root)
  8. {
  9. if (root == nullptr)
  10. {
  11. return;
  12. }
  13. _InOrder(root->_left);
  14. cout << root->_kv.first << ":" << root->_kv.second << endl;
  15. _InOrder(root->_right);
  16. }

4.5 查找

  1. Node* Find(const K& key)
  2. {
  3. Node* cur = _root;
  4. while (cur)
  5. {
  6. if (cur->_kv.first < key)
  7. {
  8. cur = cur->_right;
  9. }
  10. else if (cur->_kv.first > key)
  11. {
  12. cur = cur->_left;
  13. }
  14. else
  15. {
  16. return cur;
  17. }
  18. }
  19. }

4.6 析构

  1. ~RBTree()
  2. {
  3. _Destroy(_root);
  4. _root == nullptr;
  5. }
  6. private:
  7. void _Destroy(Node* root)
  8. {
  9. if (root == nullptr)
  10. {
  11. return;
  12. }
  13. _Destroy(root->_left);
  14. _Destroy(root->_right);
  15. delete root;
  16. root == nullptr;
  17. }

五,红黑树源代码和测试代码

源代码(RBTree.h)

  1. #pragma once
  2. //最长路径不超过最短路径的两倍
  3. //红黑树和AVL树相比来说,红黑树的优点是旋转的次数更少
  4. //如果两个树都插入1万个树,查找时,AVL树最多要查找30次,红黑树最多查找60次
  5. // 但是这种差别对于CPU来说可以忽略不计,所以论综合性能,还是红黑树更胜一筹
  6. #include<iostream>
  7. #include<assert.h>
  8. using namespace std;
  9. //用枚举来标识颜色
  10. enum Colour
  11. {
  12. RED,
  13. BLACK
  14. };
  15. template<class K, class V>
  16. struct RBTreeNode
  17. {
  18. RBTreeNode<K, V>* _left;
  19. RBTreeNode<K, V>* _right;
  20. RBTreeNode<K, V>* _parent;
  21. pair<K, V> _kv;
  22. Colour _col;
  23. RBTreeNode(const pair<K, V>& kv)
  24. :_left(nullptr)
  25. , _right(nullptr)
  26. , _parent(nullptr)
  27. , _kv(kv)
  28. , _col(RED)
  29. {}
  30. };
  31. template<class K, class V>
  32. class RBTree
  33. {
  34. typedef RBTreeNode<K, V> Node;
  35. public:
  36. Node* Find(const K& key)
  37. {
  38. Node* cur = _root;
  39. while (cur)
  40. {
  41. if (cur->_kv.first < key)
  42. {
  43. cur = cur->_right;
  44. }
  45. else if (cur->_kv.first > key)
  46. {
  47. cur = cur->_left;
  48. }
  49. else
  50. {
  51. return cur;
  52. }
  53. }
  54. }
  55. ~RBTree()
  56. {
  57. _Destroy(_root);
  58. _root == nullptr;
  59. }
  60. //插入时和搜索树一样的插入
  61. bool Insert(const pair<K, V>& kv)
  62. {
  63. if (_root == nullptr)
  64. {
  65. _root = new Node(kv);
  66. _root->_col = BLACK;
  67. return true;
  68. }
  69. Node* parent = nullptr;
  70. Node* cur = _root;
  71. while (cur)
  72. {
  73. if (cur->_kv.first < kv.first)
  74. {
  75. parent = cur;
  76. cur = cur->_right;
  77. }
  78. else if (cur->_kv.first > kv.first)
  79. {
  80. parent = cur;
  81. cur = cur->_left;
  82. }
  83. else
  84. {
  85. return false;
  86. }
  87. }
  88. cur = new Node(kv);
  89. if (parent->_kv.first < kv.first)
  90. {
  91. parent->_right = cur;
  92. }
  93. else
  94. {
  95. parent->_left = cur;
  96. }
  97. cur->_parent = parent;
  98. //插入完成,开始改变颜色和调整旋转
  99. cur->_col = RED;//把新插入的节点都搞成红色,因为如果插入后改为黑色,一定违反规则四:每条路径的黑色节点数量相同
  100. //如果父亲是黑色节点或者插入前是空树,直接摊牌不玩了,不进入循环
  101. while (parent && parent->_col == RED)//父节点存在且父节点为红色
  102. {
  103. //找祖父
  104. Node* grandfather = parent->_parent;
  105. assert(grandfather);
  106. assert(grandfather->_col == BLACK);
  107. //红黑树的关键看叔叔,也就是父亲的兄弟
  108. if (parent == grandfather->_left)//父亲在爷爷的左边,叔叔分为几种情况分开讨论
  109. {
  110. Node* uncle = grandfather->_right;//父亲在左边,那么叔叔就在右边
  111. //情况一:uncle存在且为红
  112. if (uncle && uncle->_col == RED)
  113. {
  114. //父亲和叔叔变黑是为了替代祖父的黑,祖父变红是为了保持黑色节点数量不变
  115. parent->_col = uncle->_col = BLACK;//把父亲和叔叔变黑
  116. grandfather->_col = RED;//把祖父变红
  117. //继续往上处理 -- 将祖父当成新增节点,循环往上处理
  118. cur = grandfather;
  119. parent = cur->_parent;
  120. }
  121. //情况二+三,uncle不存在 + 存在且为黑
  122. else
  123. {
  124. //新增在左边:右单旋+变色
  125. // g p
  126. // p u --> c g
  127. //c u
  128. if (cur == parent->_left)
  129. {
  130. RotateR(grandfather);
  131. parent->_col = BLACK;
  132. grandfather->_col = RED;
  133. }
  134. //新增在右边:左单旋 + 右双旋+变色
  135. // g
  136. // p u -->
  137. // c
  138. else
  139. {
  140. RotateL(parent);
  141. RotateR(grandfather);
  142. cur->_col = BLACK;
  143. grandfather->_col = RED;
  144. }
  145. break;
  146. }
  147. }
  148. else//父亲在右边,叔叔可能在左边 (parent == grandfather->_right)
  149. {
  150. Node* uncle = grandfather->_left;
  151. //情况一
  152. if (uncle && uncle->_col == RED)
  153. {
  154. parent->_col = uncle->_col = BLACK;
  155. grandfather->_col = RED;
  156. //继续往上处理
  157. cur = grandfather;
  158. parent = cur->_parent;
  159. }
  160. else //情况二+三,uncle不存在 + 存在且为黑
  161. {
  162. //新增在右边:左单旋+变色
  163. // g
  164. // u p
  165. // c
  166. if (cur == parent->_right)
  167. {
  168. RotateL(grandfather);
  169. parent->_col = BLACK;
  170. grandfather->_col = RED;
  171. }
  172. //新增在左边:左右双旋+变色
  173. // g
  174. // u p
  175. // c
  176. else
  177. {
  178. RotateR(parent);
  179. RotateL(grandfather);
  180. cur->_col = BLACK;
  181. grandfather->_col = RED;
  182. }
  183. break;
  184. }
  185. }
  186. }
  187. //循环结束
  188. _root->_col = BLACK;
  189. return true;
  190. }
  191. void InOrder()
  192. {
  193. _InOrder(_root);
  194. cout << endl;
  195. }
  196. bool IsBalance() //根据规则来检查
  197. {
  198. if (_root && _root->_col == RED)
  199. {
  200. cout << "根节点不是黑色" << endl;
  201. return false;
  202. }
  203. //定义基准值,用来判断每条路径的黑色节点数量是否相同
  204. int benchmark = 0;
  205. Node* cur = _root;
  206. while (cur)
  207. {
  208. if (cur->_col == BLACK)
  209. {
  210. ++benchmark;
  211. }
  212. cur = cur->_left;
  213. }
  214. //检查连续红色节点
  215. return _Check(_root,0,benchmark);
  216. }
  217. private:
  218. bool _Check(Node* root,int blackNum,int benchmark)
  219. {
  220. if (root == nullptr)
  221. {
  222. if (benchmark != blackNum)
  223. {
  224. cout << "某条路径黑色节点数量不相等";
  225. return false;
  226. }
  227. return true;
  228. }
  229. if (root->_col == BLACK)
  230. {
  231. ++blackNum;
  232. }
  233. if (root->_col == RED && root->_parent && root->_parent->_col == RED)
  234. {
  235. cout << "存在连续红节点" << endl;
  236. return false;
  237. }
  238. return _Check(root->_left,blackNum,benchmark) && _Check(root->_right,blackNum,benchmark);
  239. }
  240. //左旋转函数
  241. void RotateL(Node* parent)
  242. {
  243. Node* subR = parent->_right;
  244. Node* subRL = subR->_left;
  245. parent->_right = subRL;
  246. //subRL可能是空
  247. if (subRL)
  248. {
  249. subRL->_parent = parent;
  250. }
  251. //记录一下要旋转的parent节点的_parent,用于当parent是子树根时的调整
  252. Node* ppNode = parent->_parent;
  253. subR->_left = parent;
  254. parent->_parent = subR;
  255. //parent是整棵树的根
  256. if (_root == parent)
  257. {
  258. _root = subR;
  259. subR->_parent = nullptr;
  260. }
  261. else//parent是子树的根
  262. {
  263. if (ppNode->_left == parent)
  264. {
  265. ppNode->_left = subR;
  266. }
  267. else
  268. {
  269. ppNode->_right = subR;
  270. }
  271. subR->_parent = ppNode;
  272. }
  273. }
  274. //右旋转函数
  275. void RotateR(Node* parent)
  276. {
  277. Node* subL = parent->_left;
  278. Node* subLR = subL->_right;
  279. parent->_left = subLR;
  280. //subLR可能是空
  281. if (subLR)
  282. {
  283. subLR->_parent = parent;
  284. }
  285. //记录一下要旋转的parent节点的_parent,用于当parent是子树根时的调整
  286. Node* ppNode = parent->_parent;
  287. subL->_right = parent;
  288. parent->_parent = subL;
  289. //parent是整颗树的根
  290. if (_root == parent)
  291. {
  292. _root = subL;
  293. subL->_parent = nullptr;
  294. }
  295. else
  296. {
  297. if (ppNode->_left == parent)
  298. {
  299. ppNode->_left = subL;
  300. }
  301. else
  302. {
  303. ppNode->_right = subL;
  304. }
  305. subL->_parent = ppNode;
  306. }
  307. }
  308. //打印子函数
  309. void _InOrder(Node* root)
  310. {
  311. if (root == nullptr)
  312. {
  313. return;
  314. }
  315. _InOrder(root->_left);
  316. cout << root->_kv.first << ":" << root->_kv.second << endl;
  317. _InOrder(root->_right);
  318. }
  319. void _Destroy(Node* root)
  320. {
  321. if (root == nullptr)
  322. {
  323. return;
  324. }
  325. _Destroy(root->_left);
  326. _Destroy(root->_right);
  327. delete root;
  328. root == nullptr;
  329. }
  330. private:
  331. Node* _root = nullptr;
  332. };

测试

  1. #include"RBTree.h"
  2. void TestRBTree1()
  3. {
  4. int a[] = { 4,2,6,1,3,5,15,7,16,14 };
  5. RBTree<int, int> t1;
  6. for (auto e : a)
  7. {
  8. t1.Insert(make_pair(e, e));
  9. }
  10. cout << "IsBalance:" << t1.IsBalance() << endl;
  11. }
  12. void TestRBTree2()
  13. {
  14. size_t N = 10000;
  15. srand(time(0));
  16. RBTree<int, int> t1;
  17. for (size_t i = 0; i < N; ++i)
  18. {
  19. int x = rand();
  20. cout << "Insert:" << x << ":" << i << endl;
  21. t1.Insert(make_pair(x, i));
  22. }
  23. cout << "IsBalance:" << t1.IsBalance() << endl;
  24. }
  25. int main()
  26. {
  27. TestRBTree2();
  28. cout << endl;
  29. TestRBTree1();
  30. return 0;
  31. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/50980
推荐阅读
相关标签
  

闽ICP备14008679号