当前位置:   article > 正文

C++:红黑树_c++红黑树

c++红黑树

目录

一.红黑树介绍

1.红黑树的概念

2.红黑树的性质

思考:如何保证最长的路径不超过最短路径的2倍?

3.小练习

4.黑高

5.红黑树和AVL树比较

4.新增节点给红色

5.红黑树的插入操作

情况一: 红叔:叔父爷变色,爷变新结点。(cur为红,p为红,g为黑,u存在且为红)

 (1)具体情况1

 (2)具体情况2:相当于重复具体情况1

情况一: 黑叔LL型:右单旋,父,爷变色。(cur为红,p为红,g为黑,u存在且为红)

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑,但是cur是p的左

 (1)具体情况1:cur为红,p为红,g为黑,u不存在,但是cur是p的左

(2)具体情况2:cur为红,p为红,g为黑,u存在且为黑,但是cur是p的左

情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑,但是cur是p的右

 (1)具体情况1:cur为红,p为红,g为黑,u不存在,但是cur是p的右

6.检查红黑树合法性

红黑树总代码

二. 插入红黑树的完整实例

1.实例

(1)黑叔LL型。右单旋,父,爷变色。

(2)红叔:叔父爷变色,爷变新结点。

(3)黑叔RR型。左单旋,父,爷变色。

(3)黑叔LR型。左,右双旋,儿,爷变色

2.与“黑高”相关的推论

3.删除操作(不考)


 

 

一.红黑树介绍

a9ac02b872b2422395a320671b4184a4.png

1.红黑树的概念

红黑树,是一种 二叉搜索树,但 在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对 任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是 接近平衡的。( 最长的路径不超过最短路径的2倍,近似平衡
 
 
14d05351b42d48bcbf431a237eb932a1.png

 

2.红黑树的性质

1. 每个结点不是红色就是黑色
2. 根节点是黑色的 
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的  (没有连续的红色节点)
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点 ( 每条路径都包含相同数量黑色节点)
5. 每个叶子结点都是黑色的(此处的叶子结点指的是 空结点)NULL节点
9df6b6e9bccb40bcb7a4f88552023f60.png

思考:如何保证最长的路径不超过最短路径的2倍?

为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点 个数的两倍?
 
最长的路径不超过最短路径的2倍
最短:全黑
最长:一黑一红间隔
 

3.小练习

1840b60eb3b94062a0ae91a7ce3080d5.png

 86388f4e729a471c8102bae8d442efa2.png

730abf6765ac45bbb9669a71d2c46287.png 

4.黑高

ea9bce2f43954e629549f0b5e624a70c.png

5.红黑树和AVL树比较

100w数据
AVL树高度: 20行左右
红黑树高度: 40行左右,但旋转次数少很多
 

4.新增节点给红色

新增节点是红色,可能破坏3.(没有连续的红色节点
新增节点是黑色,一定破坏4.(每条路径都包含相同数量黑色节点),规则4很难维护
因此新插入的节点都染成红色,因为他下面有黑色的null叶节点,因此不是叶节点,不用给成黑色。

5.红黑树的插入操作

规则:(染色=变色)

c44536917b954dd5b71a828fa1be8459.png

 叶子都是黑色的null节点,下面没有画出来而已。

情况一: 红叔:叔父爷变色,爷变新结点。(cur为红,p为红,g为黑,u存在且为红)

对应上面的 红叔 情况,叔父爷变色,爷变为新节点。即:叔父p,u变黑色,爷g变红。

如果g是子树,则继续将g当成cur,继续向上调整;若如果g是,则“爷变新结点”,即:将爷g当作新节点,又回到上面判断新节点的条件:新结点是根—染为黑色,新结点非根—染为红色。则将g染为黑色即可。

739ea3be9bc5453aa4a68161fd7cf768.png

 (1)具体情况1

a、b、c、d、e都是空,则它们都是黑色的null叶节点。

5a15f32658ce46c0b022c0c5c07293a5.png

 (2)具体情况2:相当于重复具体情况1

cur是新增节点,将p,u改为黑,g改为红,把g当成cur,继续向上调整。此时g/cur 这个节点看成新增节点,把他的父pp亲和叔叔uu改成黑色,gg改成红色,然后如果是子树就还能继续像这样向上调整;如果是根

bbf686d8f25d4d64b8cbc6272cc65c57.png

 

d050c0eaa77a44fc95733c6b6da44bb5.png 

 

情况一: 黑叔LL型:右单旋,父,爷变色。(cur为红,p为红,g为黑,u存在且为红)

对应上面的 黑叔 情况,叔父爷变色,爷变为新节点。即:叔父p,u变黑色,爷g变红。

如果g是子树,则继续将g当成cur,继续向上调整;若如果g是,则“爷变新结点”,即:将爷g当作新节点,又回到上面判断新节点的条件:新结点是根—染为黑色,新结点非根—染为红色。则将g染为黑色即可。

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑但是cur是p的左

 (1)具体情况1:cur为红,p为红,g为黑,u不存在,但是cur是p的左

p,g右单旋后,把p改成黑色,g改成红色。因为这个局部子树的根节点p是黑色,所以跟上面的数颜色也没关系,所以不用继续向上调整

861d6bf169bd46e0ae51d31ed33e6f49.png

(2)具体情况2:cur为红,p为红,g为黑,u存在且为黑,但是cur是p的左

通过上面的分析,u如果存在且为黑那么cur原来一定是黑色的,作为下面子树的祖父,由情况一变更过来,原来是黑色,现在变红。

操作是右单旋p,g:p的右给g的左,把g再给p的右,p做根节点。最后p和g变色,p变黑,g变红

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反p为g的右孩子,cur为p的右孩子,则进行左单旋转

 

9da98774cf0d4ea391740f221babef23.png

x y m n几个位置中某个位置子树插入红节点,引发cur从黑变红
xymnabd e等子树一定是包含相同数量黑色节点的红黑树子树

①到②是情况一cur(y位置)为红,p为红,g为黑,u存在且为红的变化,②到③才是这里的 cur为红,p为红,g为黑,u存在且为黑 的情况,操作是右单旋p,g,g变红,p变黑。

83d0ca2326e34a80ba54767a4bf89c5a.png

情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑但是cur是p的右

情况二和情况三的区别

情况二:p是g的左,cur是p的左 -- 单旋
情况三:p是g的左,cur是p的右 -- 双旋

 

p g 的左孩子, cur p 的右孩子,则针对 p 做左单旋转;相反,
p g 的右孩子, cur p 的左孩子,则针对 p 做右单旋转
则转换成了情况 2
①到②p和cur进行左单旋,②到③把cur和p交换,最终转换成了情况二
 

c0b2353bd2a248768c28b9f904531b96.png

 (1)具体情况1:cur为红,p为红,g为黑,u不存在,但是cur是p的右

双旋+变色

 577ac4f97a954ddf88366250ba4d9e17.png

 

 

  (2)具体情况2:cur为红,p为红,g为黑,u存在且为黑,但是cur是p的右

 258f09e128414879a81f0b54452cfb6b.png

 

 

6.检查红黑树合法性

1、遇到红色节点就检查孩子,但是建议改成遇到红色节点检查父亲,这样好实现。(遇到红色节点就检查孩子不好检查,因为孩子可能为空可能不为空)
2、每条路径黑色节点如何检查?(下图有11条路径,走到空为一条路径)
前序递归遍历树,求出每条路径黑色节点的数量

8ae36be9c8ce435b84548268da323d69.png

  1. // 检测是否有连续红色节点和各路径黑节点个数是否相同 的支线函数
  2. bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount)
  3. {//k用来记录每条路径中黑色节点的个数, blackCount是主要函数传进来的“任意一个路径的黑色
  4. //节点个数”作基准值,只要有k和基准值比较不同就违反了性质四:每条路径中黑色节点的个数必须相同
  5. //走到null之后,k就是整个路径的黑色节点个数,判断k和black是否相等
  6. if (nullptr == pRoot)
  7. {
  8. if (k != blackCount)
  9. {
  10. cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
  11. return false;
  12. }
  13. return true;
  14. }
  15. // 统计黑色节点的个数
  16. if (BLACK == pRoot->_col)
  17. k++;
  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. // 1.只要是空树就是红黑树
  32. if (nullptr == pRoot)
  33. return true;
  34. // 2.检测根节点是否满足情况
  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. // 检测是否有连续红色节点和各路径黑节点个数是否相同
  50. size_t k = 0; //k用来记录路径中黑色节点的个数
  51. return _IsValidRBTree(pRoot, k, blackCount);
  52. }

 

 

 

红黑树总代码

 

  1. #pragma once
  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. :_kv(kv)
  17. , _left(nullptr)
  18. , _right(nullptr)
  19. , _parent(nullptr)
  20. , _col(RED)
  21. {}
  22. };
  23. template<class K, class V>
  24. struct RBTree
  25. {
  26. typedef RBTreeNode<K, V> Node;
  27. public:
  28. bool Insert(const pair<K, V>& kv)
  29. {
  30. // 1、搜索树的规则插入
  31. // 2、看是否违反平衡规则,如果违反就需要处理:旋转
  32. if (_root == nullptr)
  33. {
  34. _root = new Node(kv);
  35. _root->_col = BLACK;
  36. return true;
  37. }
  38. Node* parent = nullptr;
  39. Node* cur = _root;
  40. while (cur)
  41. {
  42. if (cur->_kv.first < kv.first)
  43. {
  44. parent = cur;
  45. cur = cur->_right;
  46. }
  47. else if (cur->_kv.first > kv.first)
  48. {
  49. parent = cur;
  50. cur = cur->_left;
  51. }
  52. else
  53. {
  54. return false;
  55. }
  56. }
  57. cur = new Node(kv);
  58. cur->_col = RED;
  59. if (parent->_kv.first < kv.first)
  60. {
  61. parent->_right = cur;
  62. }
  63. else
  64. {
  65. parent->_left = cur;
  66. }
  67. cur->_parent = parent;
  68. // 存在连续红色节点
  69. while (parent && parent->_col == RED)
  70. {
  71. Node* grandfater = parent->_parent;
  72. assert(grandfater);
  73. if (grandfater->_left == parent)
  74. {
  75. Node* uncle = grandfater->_right;
  76. // 情况一:
  77. if (uncle && uncle->_col == RED) // 叔叔存在且为红
  78. {
  79. // 变色
  80. parent->_col = uncle->_col = BLACK;
  81. grandfater->_col = RED;
  82. // 继续往上处理
  83. cur = grandfater;
  84. parent = cur->_parent;
  85. }
  86. else // 叔叔不存在 或者 叔叔存在且为黑
  87. {
  88. if (cur == parent->_left) // 单旋
  89. {
  90. // g
  91. // p
  92. // c
  93. RotateR(grandfater);
  94. parent->_col = BLACK;
  95. grandfater->_col = RED;
  96. }
  97. else // 双旋
  98. {
  99. // g
  100. // p
  101. // c
  102. RotateL(parent);
  103. RotateR(grandfater);
  104. cur->_col = BLACK;
  105. grandfater->_col = RED;
  106. }
  107. break;
  108. }
  109. }
  110. else //(grandfater->_right == parent)
  111. {
  112. Node* uncle = grandfater->_left;
  113. // 情况一:
  114. if (uncle && uncle->_col == RED)
  115. {
  116. // 变色
  117. parent->_col = uncle->_col = BLACK;
  118. grandfater->_col = RED;
  119. // 继续往上处理
  120. cur = grandfater;
  121. parent = cur->_parent;
  122. }
  123. else
  124. {
  125. if (cur == parent->_right)
  126. {
  127. // g
  128. // p
  129. // c
  130. RotateL(grandfater);
  131. parent->_col = BLACK;
  132. grandfater->_col = RED;
  133. }
  134. else // 双旋
  135. {
  136. // g
  137. // p
  138. // c
  139. RotateR(parent);
  140. RotateL(grandfater);
  141. cur->_col = BLACK;
  142. grandfater->_col = RED;
  143. }
  144. break;
  145. }
  146. }
  147. }
  148. _root->_col = BLACK;
  149. return true;
  150. }
  151. vector<vector<int>> levelOrder() {
  152. vector<vector<int>> vv;
  153. if (_root == nullptr)
  154. return vv;
  155. queue<Node*> q;
  156. int levelSize = 1;
  157. q.push(_root);
  158. while (!q.empty())
  159. {
  160. // levelSize控制一层一层出
  161. vector<int> levelV;
  162. while (levelSize--)
  163. {
  164. Node* front = q.front();
  165. q.pop();
  166. levelV.push_back(front->_kv.first);
  167. if (front->_left)
  168. q.push(front->_left);
  169. if (front->_right)
  170. q.push(front->_right);
  171. }
  172. vv.push_back(levelV);
  173. for (auto e : levelV)
  174. {
  175. cout << e << " ";
  176. }
  177. cout << endl;
  178. // 上一层出完,下一层就都进队列
  179. levelSize = q.size();
  180. }
  181. return vv;
  182. }
  183. void RotateL(Node* parent)
  184. {
  185. Node* subR = parent->_right;
  186. Node* subRL = subR->_left;
  187. parent->_right = subRL;
  188. if (subRL)
  189. subRL->_parent = parent;
  190. Node* ppNode = parent->_parent;
  191. subR->_left = parent;
  192. parent->_parent = subR;
  193. if (parent == _root)
  194. {
  195. _root = subR;
  196. _root->_parent = nullptr;
  197. }
  198. else
  199. {
  200. if (parent == ppNode->_left)
  201. {
  202. ppNode->_left = subR;
  203. }
  204. else
  205. {
  206. ppNode->_right = subR;
  207. }
  208. subR->_parent = ppNode;
  209. }
  210. }
  211. void RotateR(Node* parent)
  212. {
  213. Node* subL = parent->_left;
  214. Node* subLR = subL->_right;
  215. parent->_left = subLR;
  216. if (subLR)
  217. subLR->_parent = parent;
  218. Node* ppNode = parent->_parent;
  219. subL->_right = parent;
  220. parent->_parent = subL;
  221. if (parent == _root)
  222. {
  223. _root = subL;
  224. _root->_parent = nullptr;
  225. }
  226. else
  227. {
  228. if (ppNode->_left == parent)
  229. {
  230. ppNode->_left = subL;
  231. }
  232. else
  233. {
  234. ppNode->_right = subL;
  235. }
  236. subL->_parent = ppNode;
  237. }
  238. }
  239. int _maxHeight(Node* root)
  240. {
  241. if (root == nullptr)
  242. return 0;
  243. int lh = _maxHeight(root->_left);
  244. int rh = _maxHeight(root->_right);
  245. return lh > rh ? lh + 1 : rh + 1;
  246. }
  247. int _minHeight(Node* root)
  248. {
  249. if (root == nullptr)
  250. return 0;
  251. int lh = _minHeight(root->_left);
  252. int rh = _minHeight(root->_right);
  253. return lh < rh ? lh + 1 : rh + 1;
  254. }
  255. void _InOrder(Node* root)
  256. {
  257. if (root == nullptr)
  258. return;
  259. _InOrder(root->_left);
  260. cout << root->_kv.first << " ";
  261. _InOrder(root->_right);
  262. }
  263. bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount)
  264. {
  265. //走到null之后,判断k和black是否相等
  266. if (nullptr == pRoot)
  267. {
  268. if (k != blackCount)
  269. {
  270. cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
  271. return false;
  272. }
  273. return true;
  274. }
  275. // 统计黑色节点的个数
  276. if (BLACK == pRoot->_col)
  277. k++;
  278. // 检测当前节点与其双亲是否都为红色
  279. if (RED == pRoot->_col && pRoot->_parent && pRoot->_parent->_col == RED)
  280. {
  281. cout << "违反性质三:存在连在一起的红色节点" << endl;
  282. return false;
  283. }
  284. return _IsValidRBTree(pRoot->_left, k, blackCount) &&
  285. _IsValidRBTree(pRoot->_right, k, blackCount);
  286. }
  287. public:
  288. void InOrder()
  289. {
  290. _InOrder(_root);
  291. cout << endl;
  292. }
  293. void Height()
  294. {
  295. cout << "最长路径:" << _maxHeight(_root) << endl;
  296. cout << "最短路径:" << _minHeight(_root) << endl;
  297. }
  298. bool IsBalanceTree() //检查红黑树合法性的主要函数
  299. {
  300. // 检查红黑树几条规则
  301. Node* pRoot = _root;
  302. // 1.只要是空树就是红黑树
  303. if (nullptr == pRoot)
  304. return true;
  305. // 2.检测根节点是否满足情况
  306. if (BLACK != pRoot->_col)
  307. {
  308. cout << "违反红黑树性质二:根节点必须为黑色" << endl;
  309. return false;
  310. }
  311. // 获取任意一条路径中黑色节点的个数 -- 比较基准值
  312. size_t blackCount = 0;
  313. Node* pCur = pRoot;
  314. while (pCur)
  315. {
  316. if (BLACK == pCur->_col)
  317. blackCount++;
  318. pCur = pCur->_left;
  319. }
  320. // 检测是否有连续红色节点和各路径黑节点个数是否相同
  321. size_t k = 0; //k用来记录路径中黑色节点的个数
  322. return _IsValidRBTree(pRoot, k, blackCount);
  323. }
  324. private:
  325. Node* _root = nullptr;
  326. };
  327. void TestRBTree1()
  328. {
  329. //int a[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
  330. int a[] = { 30, 29, 28, 27, 26, 25, 24, 11, 8, 7, 6, 5, 4, 3, 2, 1 };
  331. RBTree<int, int> t;
  332. for (auto e : a)
  333. {
  334. t.Insert(make_pair(e, e));
  335. }
  336. t.levelOrder();
  337. t.InOrder();
  338. t.Height();
  339. }
  340. void TestRBTree2()
  341. {
  342. const size_t N = 1024 * 1024;
  343. vector<int> v;
  344. v.reserve(N);
  345. srand(time(0));
  346. for (size_t i = 0; i < N; ++i)
  347. {
  348. v.push_back(rand());
  349. //v.push_back(i);
  350. }
  351. RBTree<int, int> t;
  352. for (auto e : v)
  353. {
  354. t.Insert(make_pair(e, e));
  355. }
  356. //t.levelOrder();
  357. //cout << endl;
  358. cout << "是否平衡?" << t.IsBalanceTree() << endl;
  359. t.Height();
  360. //t.InOrder();
  361. }

二. 插入红黑树的完整实例

1.实例

c44536917b954dd5b71a828fa1be8459.png

(1)黑叔LL型。右单旋,父,爷变色。

对应上面的 黑叔LL型 情况,右单旋,父,爷变色。

5是新插入节点—儿子,此时父10是爷20的左L,儿子5是父10的左L,所以是黑叔LL型,父10和爷20右单旋,10的右给20的左边,把20给10的右,10去爷爷的位置。再变色,把父亲10和爷爷20变色。

12c94ec71cb64165b4f255025396be80.png

(2)红叔:叔父爷变色,爷变新结点。

30是新插入节点—儿子,让爷10变红,叔5和父20变黑,爷10变新结点,如果爷10是根节点,就回到上面判断新节点的条件:新结点是根—染为黑色,新结点非根—染为红色。这里爷10是根,则将10染为黑色即可。

8bb5c9982c61479bb533eb167a3bd544.png

(3)黑叔RR型。左单旋,父,爷变色。

对应上面的 黑叔RR型 情况,左单旋,父,爷变色。

40是新插入节点—儿子,此时父30是爷20的右R,儿子40是父30的右R,并且叔叔是null黑节点,所以是黑叔RR型,父30和爷20左单旋,30的左给20的右,把20给30的左,30去爷爷的位置。再变色,把父亲30和爷爷20变色,20变红,30变黑。因为这个局部子树的根节点30是黑色,所以跟上面的数颜色也没关系,所以不用继续向上调整
8ebdb63baf304b9c8b03988b6208f598.png

又是红叔:叔父爷变色,爷变新结点。


11563b00b83a44888c27148ad047c192.png
当把“爷变为新结点”后,还需要继续按照红黑树的定义进行判断和调整。
d216a07de4f9480099ee57aa8bc078b3.png
6c52b9c912d2450594adb9dbf4c6e275.png
05c62e075df7474bb8b9220118563288.png

(3)​​​​​​​黑叔LR型。左,右双旋,儿,爷变色

对应上面的 黑叔LR型 情况,左,右双旋,儿,爷变色

23是新插入节点—儿子,此时父22是爷爷25的左L,儿子23是父22的右R,并且叔叔是null黑节点,所以是黑叔LR型,进行左右双旋


127a7cd0d6e5489ab7b59bc819f0a804.png

父22和儿子23先左单旋:把23的左给父亲22的右,把父亲22给儿子23的左,儿子23给爷爷25的左。
8f2317ea600445408825cc8c73dff725.png

儿子23和爷爷25右单旋:把23的右给25的左,把25给儿子23的右,儿子23给20的右。


4321f75696dd43b0b54834ddc063c117.png

再变色,把儿子23和爷爷25变色,爷爷25变红,儿子23变黑。因为这个局部子树的根节点23是黑色,所以跟上面的数颜色也没关系,所以不用继续向上调整。​​​​​​​
72c67ca1a6284eeb9e28113b5a858e67.png


其余情况都类似,就不赘述了。
37ab56f67cd641e39012696e94f10cdc.png

2.与“黑高”相关的推论

699ec707b87c4206aac0594da2c8a5ae.png
内部结点不包括叶子结点,即NULL那些结点。
d8d1a372bd1e479d9ddf47dd76d55b1c.png
81539f94e5cf4ebbbe875f0f80664e14.png

3.删除操作(不考)

ec448053b7eb4132a1dc7bd0e5c2ea03.png

 

 

 

 

 

 

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/245975
推荐阅读
相关标签
  

闽ICP备14008679号