深圳幻海软件技术有限公司 欢迎您!

【数据结构】手撕红黑树

2023-03-14

目录一、红黑树简介1、红黑树的简介2、红黑树的性质二、红黑树的插入(看叔叔的颜色就行)1、为什么新插入的节点必须给红色?2、插入红色节点后,判定红黑树性质是否被破坏2.1情况一:uncle存在且为红2.2情况二:uncle不存在/存在且为黑(直线)2.3情况三:uncle不存在/存在且为黑(折线)2


目录

一、红黑树简介

1、红黑树的简介

2、红黑树的性质

二、红黑树的插入(看叔叔的颜色就行)

1、为什么新插入的节点必须给红色?

2、插入红色节点后,判定红黑树性质是否被破坏

2.1情况一:uncle存在且为红

2.2情况二:uncle不存在/存在且为黑(直线)

2.3情况三:uncle不存在/存在且为黑(折线)

2.4总结

3、红黑树插入代码

三、红黑树的平衡检测

四、红黑树整体代码


一、红黑树简介

1、红黑树的简介

红黑树和AVL树一样,因其逻辑复杂,面试时现场要求手撕就是纯纯刁难面试者。但某大厂面试官曾要求某些求职者现场手撕红黑树(我赌5毛,让面试官撕,他也撕不出来,而且你家员工上班手搓红黑树啊?),随后求职遭遇被发到网上吐槽,这便有了“手撕红黑树”的梗,也让红黑树成为了知名度最高的数据结构。(话虽如此,对于红黑树的性质、插入思想等概念还是需要掌握的)

2、红黑树的性质

红黑树本质也是一种二叉搜索树。底层结构需要使用二叉搜索树的地方,基本上都会使用红黑树来实现,而AVL树也因此坐上了冷板凳。

红黑树通过在每个节点上添加一个存储位,用于存储“RED”或“BLACK”。通过节点上红/黑颜色限制,确保最长路径不超过最短路径的两倍,因而它是接近平衡的树形结构。最短路径:全黑;最长路径:一黑一红交替。

1、红黑树的根节点是黑色的;

2、没有连续的红色节点(如果某个节点为红色,则它的左右孩子必须是黑色)

3、无论哪个节点,其每条路径的黑色节点数量相同;

4、所有的空节点(NIL节点)可以认为是黑色的。

最优情况:全黑或每条路径都是一黑一红的满二叉树,高度logN

最差情况:每颗子树左子树全黑,右子树一黑一红。高度2*logN。

可以发现,最坏情况的时间复杂度和AVL树一样,都是O(logN),但是红黑树这种近似平衡的结构减少了大量旋转,综合性能优于AVL树。

二、红黑树的插入(看叔叔的颜色就行)

1、为什么新插入的节点必须给红色?

新节点给红色,可能会违反上面说的红黑树性质2;如果新节点给黑色,必定会违反性质3。

2、插入红色节点后,判定红黑树性质是否被破坏

情况一调整后可能变成情况一、情况二、情况三。

2.1情况一:uncle存在且为红

这种情况cur、parent、grandfather都是确定颜色的,唯独uncle的颜色是不确定的。

可以这么想:cur为红那么就需要将parent变为黑;parent变黑需要控制每条路径上黑节点的数量相同,那么就要把uncle变黑;如果grandfather不是根,需要反转为红,用以控制路径黑节点数量相同。继续向上调整即可。

2.2情况二:uncle不存在/存在且为黑(直线)

uncle的情况分两种。

uncle不存在,则cur为插入节点,单旋即可。

uncle存在且为黑是第一种情况变过来的。

2.3情况三:uncle不存在/存在且为黑(折线)

uncle的情况分两种。

uncle不存在,则cur为插入节点,两次单旋即可。

uncle存在且为黑,先掰直

2.4总结

插入新节点时,父节点为红,看叔叔的颜色。

1、叔叔存在且为红,变色,向上调整(可能变为三种情况中的任意一种)

2、叔叔不存在/存在且为黑,直线。单旋+变色

3、叔叔不存在/存在且为黑,折线,两次单旋+变色

3、红黑树插入代码

  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. //_root不为空
  10. Node* parent = nullptr;
  11. Node* cur = _root;
  12. while (cur)
  13. {
  14. if (cur->_kv.first < kv.first)
  15. {
  16. parent = cur;
  17. cur = cur->_right;
  18. }
  19. else if (cur->_kv.first > kv.first)
  20. {
  21. parent = cur;
  22. cur = cur->_left;
  23. }
  24. else//相等说明元素相同,插入失败
  25. return false;
  26. }
  27. //开始插入
  28. cur = new Node(kv);
  29. cur->_col = RED;//新插入节点给红色,可能违反规则。如果给黑色会导致其他路径的黑色节点数量不相同,必定违反规则。
  30. if (parent->_kv.first < kv.first)
  31. {
  32. parent->_right = cur;
  33. cur->_parent = parent;//维护cur的父指针
  34. }
  35. else
  36. {
  37. parent->_left = cur;
  38. cur->_parent = parent;
  39. }
  40. //调整
  41. while (parent&&parent->_col == RED)
  42. {
  43. Node* grandfather = parent->_parent;//找到祖父
  44. if (grandfather->_left == parent)//如果父亲是祖父的左孩子
  45. {
  46. Node* uncle = grandfather->_right;//找到叔叔
  47. //情况一:叔叔存在且为红
  48. if (uncle != nullptr && uncle->_col == RED)
  49. {
  50. //变色
  51. parent->_col = uncle->_col = BLACK;
  52. grandfather->_col = RED;
  53. cur = grandfather;
  54. parent = cur->_parent;
  55. }
  56. else//情况二或情况三
  57. {
  58. if (cur == parent->_left)//情况二,直线
  59. {
  60. RotateRight(grandfather);//右单旋
  61. parent->_col = BLACK;
  62. grandfather->_col = RED;
  63. }
  64. else//情况三,折线
  65. {
  66. RotateLeft(parent);//左单旋
  67. RotateRight(grandfather);//右单旋
  68. cur->_col = BLACK;
  69. grandfather->_col = RED;
  70. }
  71. break;
  72. }
  73. }
  74. else//如果父亲是祖父的右孩子
  75. {
  76. Node* uncle = grandfather->_left;
  77. if (uncle != nullptr && uncle->_col == RED)
  78. {
  79. parent->_col =uncle->_col= BLACK;
  80. grandfather->_col = RED;
  81. cur = grandfather;
  82. parent = cur->_parent;
  83. }
  84. else
  85. {
  86. if (cur == parent->_right)//情况二,直线
  87. {
  88. //g
  89. // p
  90. // c
  91. RotateLeft(grandfather);//左单旋
  92. parent->_col = BLACK;
  93. grandfather->_col = RED;
  94. }
  95. else//情况三,折线
  96. {
  97. //g
  98. // p
  99. //c
  100. RotateRight(parent);//右单旋
  101. RotateLeft(grandfather);//左单旋
  102. cur->_col = BLACK;
  103. grandfather->_col = RED;
  104. }
  105. break;
  106. }
  107. }
  108. }
  109. _root->_col=BLACK;
  110. return true;
  111. }

三、红黑树的平衡检测

  1. bool Check(Node* root,int blackNum,const int ref)//检查有没有连续红节点
  2. {
  3. if (root == nullptr)
  4. {
  5. if (blackNum != ref)
  6. {
  7. cout << "路径上黑节点数量不一致" << endl;
  8. return false;
  9. }
  10. return true;
  11. }
  12. if (root->_col == BLACK)
  13. {
  14. ++blackNum;
  15. }
  16. if (root->_col == RED && root->_parent->_col == RED)
  17. {
  18. cout << "违反规则,父子均为红" << endl;
  19. return false;
  20. }
  21. return Check(root->_left, blackNum,ref) && Check(root->_right, blackNum, ref);
  22. }
  23. bool _IsBalance()
  24. {
  25. if (_root == nullptr)
  26. return true;
  27. if (_root->_col != BLACK)
  28. {
  29. return false;
  30. }
  31. //数一下一条路径黑色节点数量
  32. int ref = 0;//统计一条路上黑色节点的数量
  33. Node* left = _root;
  34. while (left != nullptr)
  35. {
  36. if (left->_col == BLACK)
  37. {
  38. ++ref;
  39. }
  40. left = left->_left;
  41. }
  42. return Check(_root,0,ref);
  43. }

1、在_IsBalance中确定号一条路径中黑色节点的数量,作为参数传递给Check函数,Check函数需要在递归至根节点时,统计,每条路径黑色节点数量是否和基准值ref相等。

2、Check函数中还需要判断:子节点为红,父节点也为红(此时不平衡)

四、红黑树整体代码

  1. #pragma once
  2. #include <iostream>
  3. #include <map>
  4. #include <set>
  5. #include <string>
  6. using namespace std;
  7. enum Color
  8. {
  9. RED,
  10. BLACK,
  11. };
  12. template <class K,class V>
  13. struct RBTreeNode
  14. {
  15. RBTreeNode(const pair<K,V>& kv)
  16. :_parent(nullptr)
  17. ,_left(nullptr)
  18. ,_right(nullptr)
  19. ,_kv(kv)
  20. ,_col(RED)
  21. {}
  22. RBTreeNode<K,V>* _parent;
  23. RBTreeNode<K, V>* _left;
  24. RBTreeNode<K, V>* _right;
  25. pair<K, V> _kv;
  26. Color _col;
  27. };
  28. template <class K, class V>
  29. class RBTree
  30. {
  31. public:
  32. typedef RBTreeNode<K,V> Node;
  33. bool Insert(const pair<K,V>& kv)
  34. {
  35. if (_root == nullptr)
  36. {
  37. _root = new Node(kv);
  38. _root->_col = BLACK;//根节点给黑色
  39. return true;
  40. }
  41. //_root不为空
  42. Node* parent = nullptr;
  43. Node* cur = _root;
  44. while (cur)
  45. {
  46. if (cur->_kv.first < kv.first)
  47. {
  48. parent = cur;
  49. cur = cur->_right;
  50. }
  51. else if (cur->_kv.first > kv.first)
  52. {
  53. parent = cur;
  54. cur = cur->_left;
  55. }
  56. else//相等说明元素相同,插入失败
  57. return false;
  58. }
  59. //开始插入
  60. cur = new Node(kv);
  61. cur->_col = RED;//新插入节点给红色,可能违反规则。如果给黑色会导致其他路径的黑色节点数量不相同,必定违反规则。
  62. if (parent->_kv.first < kv.first)
  63. {
  64. parent->_right = cur;
  65. cur->_parent = parent;//维护cur的父指针
  66. }
  67. else
  68. {
  69. parent->_left = cur;
  70. cur->_parent = parent;
  71. }
  72. //调整
  73. while (parent&&parent->_col == RED)
  74. {
  75. Node* grandfather = parent->_parent;//找到祖父
  76. if (grandfather->_left == parent)//如果父亲是祖父的左孩子
  77. {
  78. Node* uncle = grandfather->_right;//找到叔叔
  79. //情况一:叔叔存在且为红
  80. if (uncle != nullptr && uncle->_col == RED)
  81. {
  82. //变色
  83. parent->_col = uncle->_col = BLACK;
  84. grandfather->_col = RED;
  85. cur = grandfather;
  86. parent = cur->_parent;
  87. }
  88. else//情况二或情况三
  89. {
  90. if (cur == parent->_left)//情况二,直线
  91. {
  92. RotateRight(grandfather);//右单旋
  93. parent->_col = BLACK;
  94. grandfather->_col = RED;
  95. }
  96. else//情况三,折线
  97. {
  98. RotateLeft(parent);//左单旋
  99. RotateRight(grandfather);//右单旋
  100. cur->_col = BLACK;
  101. grandfather->_col = RED;
  102. }
  103. break;
  104. }
  105. }
  106. else//如果父亲是祖父的右孩子
  107. {
  108. Node* uncle = grandfather->_left;
  109. if (uncle != nullptr && uncle->_col == RED)
  110. {
  111. parent->_col =uncle->_col= BLACK;
  112. grandfather->_col = RED;
  113. cur = grandfather;
  114. parent = cur->_parent;
  115. }
  116. else
  117. {
  118. if (cur == parent->_right)//情况二,直线
  119. {
  120. //g
  121. // p
  122. // c
  123. RotateLeft(grandfather);//左单旋
  124. parent->_col = BLACK;
  125. grandfather->_col = RED;
  126. }
  127. else//情况三,折线
  128. {
  129. //g
  130. // p
  131. //c
  132. RotateRight(parent);//右单旋
  133. RotateLeft(grandfather);//左单旋
  134. cur->_col = BLACK;
  135. grandfather->_col = RED;
  136. }
  137. break;
  138. }
  139. }
  140. }
  141. _root->_col=BLACK;
  142. return true;
  143. }
  144. void Inorder()
  145. {
  146. _Inorder(_root);
  147. }
  148. bool IsBalance()
  149. {
  150. return _IsBalance();
  151. }
  152. private:
  153. void _Inorder(Node* root)
  154. {
  155. if (root == nullptr)
  156. return;
  157. _Inorder(root->_left);
  158. cout << root->_kv.first << ":" << root->_kv.second << endl;
  159. _Inorder(root->_right);
  160. }
  161. bool Check(Node* root,int blackNum,const int ref)//检查有没有连续红节点
  162. {
  163. if (root == nullptr)
  164. {
  165. if (blackNum != ref)
  166. {
  167. cout << "路径上黑节点数量不一致" << endl;
  168. return false;
  169. }
  170. return true;
  171. }
  172. if (root->_col == BLACK)
  173. {
  174. ++blackNum;
  175. }
  176. if (root->_col == RED && root->_parent->_col == RED)
  177. {
  178. cout << "违反规则,父子均为红" << endl;
  179. return false;
  180. }
  181. return Check(root->_left, blackNum,ref) && Check(root->_right, blackNum, ref);
  182. }
  183. bool _IsBalance()
  184. {
  185. if (_root == nullptr)
  186. return true;
  187. if (_root->_col != BLACK)
  188. {
  189. return false;
  190. }
  191. //数一下一条路径黑色节点数量
  192. int ref = 0;//统计一条路上黑色节点的数量
  193. Node* left = _root;
  194. while (left != nullptr)
  195. {
  196. if (left->_col == BLACK)
  197. {
  198. ++ref;
  199. }
  200. left = left->_left;
  201. }
  202. return Check(_root,0,ref);
  203. }
  204. void RotateLeft(Node* parent)//左单旋
  205. {
  206. Node* grandfather = parent->_parent;
  207. Node* cur = parent->_right;
  208. if (parent == _root)
  209. {
  210. _root = cur;
  211. cur->_parent = nullptr;
  212. }
  213. else
  214. {
  215. if (grandfather->_left == parent)//需要判定parent原来属于grandfather的哪一边
  216. grandfather->_left = cur;
  217. else
  218. grandfather->_right = cur;
  219. cur->_parent = grandfather;
  220. }
  221. parent->_right = cur->_left;
  222. if (cur->_left != nullptr)
  223. cur->_left->_parent = parent;
  224. cur->_left = parent;
  225. parent->_parent = cur;
  226. }
  227. void RotateRight(Node* parent)//右单旋
  228. {
  229. Node* grandfather = parent->_parent;
  230. Node* cur = parent->_left;
  231. if (parent == _root)
  232. {
  233. _root = cur;
  234. cur->_parent = nullptr;
  235. }
  236. else
  237. {
  238. if (grandfather->_left == parent)
  239. {
  240. grandfather->_left = cur;
  241. cur->_parent = grandfather;
  242. }
  243. else
  244. {
  245. grandfather->_right = cur;
  246. cur->_parent = grandfather;
  247. }
  248. }
  249. parent->_parent = cur;
  250. parent->_left = cur->_right;
  251. if (cur->_right != nullptr)
  252. cur->_right->_parent = parent;
  253. cur->_right = parent;
  254. }
  255. private:
  256. Node* _root=nullptr;
  257. };
  258. void TestAVLTree()
  259. {
  260. //int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
  261. //int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
  262. int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
  263. //int a[] = { 9,8,7,6,5,4,3,2,1};
  264. RBTree<int, int> t;
  265. for (auto e : a)
  266. {
  267. t.Insert(make_pair(e, e));
  268. }
  269. t.Inorder();
  270. //cout << t.IsBalance() << endl;
  271. }
文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览40346 人正在系统学习中
任何时间,任何地点,
微信名片