前言

  11.1新的一月加油!这个购物狂欢的季节,一看,已囊中羞涩!赶紧来恶补一下红黑树和2-3树吧!红黑树真的算是大名鼎鼎了吧?即使你不了解它,但一定听过吧?下面跟随我来揭开神秘的面纱吧!

  一、2-3树

  1、抢了红黑树的光环?

  今天的主角是红黑树,是无疑的,主角光环在呢!那2-3树又是什么鬼呢?学习2-3树不仅对理解红黑树有帮助,对理解B类树,也是有巨大帮助的,所以学习2-3树很必要!

  2、基本性质

  2-3树满足二分搜索树的基本性质,但节点可以存放一个元素或两个元素!如下图,就是2-3树:

  

  说明:2-3树一颗绝对平衡的树(绝对平衡:对于任意一个节点,左右子树高度相同)

  3、维护绝对平衡

  2-3树在插入过程中如何维护绝对平衡呢?进行画图演示,实在有点不好画,如下图:

  

  说明:

  1、不能将新节点插入到空节点

    因为那样如上图,就不满足绝对平衡了,所以可以将37和42合并,2-3支持3节点。

  2、不支持4节点,进行拆分

    再插入12时,也不能插入空节点,也要合并,但2-3树不支持4节点,所以进行进行拆分。

  3、子节点达到3节点,合并到父节点

    再依次插入18、6,达到4节点,进行拆分,但不符合绝对平衡了怎么办?将12和37合并,就形成了最后3节点的图了

  总结:讲到这里,应该对2-3树如何维护绝对平衡,应该了解了吧?理解2-3树,对于再理解红黑树,是非常有帮助的,其实,它们有等价性的,接下来会说明的。

  二、红黑树

  1、红黑树和2-3树的等价性

  也想达到像2-3树那样的绝对平衡,但2-3树的实现比较麻烦,所以产生了红黑树;那么,红黑树和2-3树有怎么样的等价性呢?如下图:

  

  说明:红黑树最开始想用红线区别b、c,但实现起来比较困难,然后用红黑来表示节点,就比较好实现了!

  红黑树和2-3树总体对比图,可以参考一下:

  

  2、红黑树5个重要性质

  1、引自《算法导论》

  红黑树有五个重要性质,引自算法界一本圣洁《算法导论》中的内容,如下:

  

  是不是看着有点晕,下面我进行解释。

  2、5个重要性质

  1、每一个节点或者红色的,或是黑色的

  2、根节点是黑色的

  3、每一个叶子节点(最后的空节点)是黑色的

  4、如果一个节点是红色的,那么它的孩子节点都是黑色的

  5、从任意节点到叶子节点,经过的黑色节点是一样的

  

  解释:最重要的性质是第五条,前4条在理解2-3树之后,就很好理解了,第5条性质说明了:红黑树是保持“黑平衡”的二叉树;

严格意义上来说,红黑树不是平衡二叉树,最大高度:2logn,但是时间复杂度仍然是O(logn),因为2是常数,但比AVL树查询要稍微慢一些。

  三、红黑树添加元素

  红黑树添加元素,比较繁琐,因为要保持上面的五个性质,要不然就不是红黑树了;

  1、保持根节点为节点

  红黑树的节点类也可以从二分搜索树上进行修改,但要新增“color”成员变量,来标注节点颜色,节点类如下:

  1. template<typename Key, typename Value>
  2. class RBTree {
  3. private:
  4. static const bool RED = true;
  5. static const bool BLACK = false;
  6.  
  7. struct Node {
  8. Key key;
  9. Value value;
  10. Node *left;
  11. Node *right;
  12. bool color;
  13.  
  14. Node(Key key, Value value) {
  15. this->key = key;
  16. this->value = value;
  17. this->left = this->right = nullptr;
  18. color = RED;  //默认初始化为红色
  19. }
  20.  
  21. Node(Node *node) {
  22. this->key = node->key;
  23. this->value = node->value;
  24. this->left = node->left;
  25. this->right = node->right;
  26. this->color = node->color;
  27. }
  28. };
  29.  
  30. Node *root;
  31. int size;
  32. }

  因为红黑树性质1要求根节点为黑色,所以要保持根节点为黑色;

  2、左旋转

  像AVL树一样,红黑树也需要左旋和右旋,如下图就需要左旋转,因为“红色节点是左倾斜的”:

  

  说明:图中黑色字体标识黑色节点,红色表示红色节点,并演示了旋转过程,最后还要改变节点颜色。

  3、左旋转代码实现

  代码如下:

  

  1. Node *leftRotate(Node *node) {
  2. Node *x = node->right;
  3. node->right = x->left;
  4. x->left = node;
  5.  
  6. x->color = node->color;
  7. node->color = RED;
  8.  
  9. return x;
  10. }

  4、颜色反转

  下面这种情况就需要颜色反转,如下图:

  

  

  5、颜色反转代码实现

  代码如下:

  1. void flipColors(Node *node) {
  2. node->color = RED;
  3. node->left->color = BLACK;
  4. node->right->color = BLACK;
  5. }

  6、右旋转

  下面情况需要右旋转,如下图:

  

    旋转之后,如下图:

  

   7、右旋转代码如下

  代码如下:

  

  1. Node *rightRotate(Node *node) {
  2. Node *x = node->left;
  3. node->left = x->right;
  4. x->right = node;
  5.  
  6. x->color = node->color;
  7. node->color = RED;
  8.  
  9. return x;
  10. }

  8、总体流程图

  

  9、总体代码

  总体代码如下,供参考和学习:

  1. #ifndef RED_BLACK_TREE_RBTREE_H
  2. #define RED_BLACK_TREE_RBTREE_H
  3.  
  4. #include <iostream>
  5. #include <vector>
  6.  
  7. template<typename Key, typename Value>
  8. class RBTree {
  9. private:
  10. static const bool RED = true;
  11. static const bool BLACK = false;
  12.  
  13. struct Node {
  14. Key key;
  15. Value value;
  16. Node *left;
  17. Node *right;
  18. bool color;
  19.  
  20. Node(Key key, Value value) {
  21. this->key = key;
  22. this->value = value;
  23. this->left = this->right = nullptr;
  24. color = RED;
  25. }
  26.  
  27. Node(Node *node) {
  28. this->key = node->key;
  29. this->value = node->value;
  30. this->left = node->left;
  31. this->right = node->right;
  32. this->color = node->color;
  33. }
  34. };
  35.  
  36. Node *root;
  37. int size;
  38.  
  39. public:
  40.  
  41. RBTree() {
  42. root = nullptr;
  43. size = ;
  44. }
  45.  
  46. ~RBTree() {
  47. destroy(root);
  48. }
  49.  
  50. int getSize() {
  51. return size;
  52. }
  53.  
  54. int isEmpty() {
  55. return size == ;
  56. }
  57.  
  58. bool isRed(Node *node) {
  59. if (node == nullptr) {
  60. return BLACK;
  61. }
  62. return node->color;
  63. }
  64.  
  65. void add(Key key, Value value) {
  66. root = add(root, key, value);
  67. root->color = BLACK;
  68. }
  69.  
  70. bool contains(Key key) {
  71. return getNode(root, key) != nullptr;
  72. }
  73.  
  74. Value *get(Key key) {
  75. Node *node = getNode(root, key);
  76. return node == nullptr ? nullptr : &(node->value);
  77. }
  78.  
  79. void set(Key key, Value newValue) {
  80. Node *node = getNode(root, key);
  81. if (node != nullptr) {
  82. node->value = newValue;
  83. }
  84. }
  85.  
  86. private:
  87.  
  88. // 向以node为根的二叉搜索树中,插入节点(key, value)
  89. // 返回插入新节点后的二叉搜索树的根
  90. Node *add(Node *node, Key key, Value value) {
  91. if (node == nullptr) {
  92. size++;
  93. return new Node(key, value);
  94. }
  95. if (key == node->key) {
  96. node->value = value;
  97. } else if (key < node->key) {
  98. node->left = add(node->left, key, value);
  99. } else {
  100. node->right = add(node->right, key, value);
  101. }
  102.  
  103. if (isRed(node->right) && !isRed(node->left)) {
  104. node = leftRotate(node);
  105. }
  106.  
  107. if (isRed(node->left) && isRed(node->left->left)) {
  108. node = rightRotate(node);
  109. }
  110.  
  111. if (isRed(node->left) && isRed(node->right)) {
  112. flipColors(node);
  113. }
  114. return node;
  115. }
  116.  
  117. // 在以node为根的二叉搜索树中查找key所对应的Node
  118. Node *getNode(Node *node, Key key) {
  119. if (node == nullptr) {
  120. return nullptr;
  121. }
  122. if (key == node->key) {
  123. return node;
  124. } else if (key < node->key) {
  125. return getNode(node->left, key);
  126. } else {
  127. return getNode(node->right, key);
  128. }
  129. }
  130.  
  131. void destroy(Node *node) {
  132. if (node != nullptr) {
  133. destroy(node->left);
  134. destroy(node->right);
  135. delete node;
  136. size--;
  137. }
  138. }
  139.  
  140. Node *leftRotate(Node *node) {
  141. Node *x = node->right;
  142. node->right = x->left;
  143. x->left = node;
  144.  
  145. x->color = node->color;
  146. node->color = RED;
  147.  
  148. return x;
  149. }
  150.  
  151. Node *rightRotate(Node *node) {
  152. Node *x = node->left;
  153. node->left = x->right;
  154. x->right = node;
  155.  
  156. x->color = node->color;
  157. node->color = RED;
  158.  
  159. return x;
  160. }
  161.  
  162. void flipColors(Node *node) {
  163. node->color = RED;
  164. node->left->color = BLACK;
  165. node->right->color = BLACK;
  166. }
  167. };
  168.  
  169. #endif //RED_BLACK_TREE_RBTREE_H

  总结  

  面试时99.9%不会让手写一下红黑树的添加过程,除非你面试算法工程师,那就打扰了!主要理解红黑树的性质、左旋和右旋等。

  欢迎点赞和评论,感谢支持!

大名鼎鼎的红黑树,你get了么?2-3树 绝对平衡 右旋转 左旋转 颜色反转的更多相关文章

  1. TreeMap----的实现原理(红黑树)

    TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap的实现,但是为了与Java提高篇系列博文保持一致 ...

  2. 论AVL树与红黑树

    首先讲解一下AVL树: 例如,我们要输入这样一串数字,10,9,8,7,15,20这样一串数字来建立AVL树 1,首先输入10,得到一个根结点10 2,然后输入9, 得到10这个根结点一个左孩子结点9 ...

  3. 红黑树(Red-Black tree)

    红黑树又称红-黑二叉树,它首先是一颗二叉树,它具体二叉树所有的特性.同时红黑树更是一颗自平衡的排序二叉树.我们知道一颗基本的二叉树他们都需要满足一个基本性质–即树中的任何节点的值大于它的左子节点,且小 ...

  4. Atitit 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树 attilax理解与总结

    Atitit 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树 attilax理解与总结 1.1. 树形结构-- 一对多的关系1 1.2. 树的相关术语: 1 1.3. 常见的树形结构 ...

  5. 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现

    本文转载自http://www.ibm.com/developerworks/cn/java/j-lo-tree/ 目录: TreeSet 和 TreeMap 的关系 TreeMap 的添加节点 Tr ...

  6. Sedgewick的红黑树

    红黑树一直是数据结构中的难点,大部分关于算法与数据结构的学习资料(包括<算法导论>)对于这部分的讲解都是上来就下定义,告诉我们红黑树这个性质那个性质,插入删除要注意1234点,但是基本没有 ...

  7. 红黑树(二)之 C语言的实现

    概要 红黑树在日常的使用中比较常用,例如Java的TreeMap和TreeSet,C++的STL,以及Linux内核中都有用到.之前写过一篇文章专门介绍红黑树的理论知识,本文将给出红黑数的C语言的实现 ...

  8. 红黑树(四)之 C++的实现

    概要 前面分别介绍红黑树的理论知识和红黑树的C语言实现.本章是红黑树的C++实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章. 目录1. 红黑树的介绍2. 红黑树的C++ ...

  9. 红黑树(五)之 Java的实现

    概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C+ ...

随机推荐

  1. 第二天 Java语言基础

    一.如何定义Java中的类 Java代码都定义在类中,类由class来定义,区分public class和class: 二.main方法的作用 main方法是程序的入口:保证程序的独立运行:被JVM调 ...

  2. 【BZOJ 3561】 DZY Loves Math VI

    题目: 给定正整数n,m.求   题解: 水题有益身心健康.(博客园的辣鸡数学公式) 其实到这我想强上伯努利数,然后发现$n^2$的伯努利数,emmmmmm 发现这个式子可以算时间复杂度,emmmmm ...

  3. bzoj 2005 能量采集 莫比乌斯反演

    我们要求的是∑ni=1∑mj=1(2×gcd(i,j)−1) 化简得2×∑ni=1∑mj=1gcd(i,j)−n×m 所以我们现在只需要求出∑ni=1∑mj=1gcd(i,j)即可 ∑ni=1∑mj= ...

  4. BZOJ_4016_[FJOI2014]最短路径树问题_最短路+点分治

    BZOJ_4016_[FJOI2014]最短路径树问题_最短路+点分治 Description 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择 ...

  5. Vue 进阶之路(五)

    之前的文章我们说了一下 vue 的样式绑定,这篇文章来介绍一下 vue 中的条件渲染,先看下面的代码: <!DOCTYPE html> <html lang="en&quo ...

  6. FreeSql v0.5.x 功能介绍

    弱类型 之前在操作实体时,必须传统泛型参数,现在可以实现弱类型实体的操作.以 Repository 为例: var repos = fsql.GetGuidRepository<object&g ...

  7. 你真的了解字典(Dictionary)吗?

    从一道亲身经历的面试题说起 半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点. 为了便于描述,我把上面的那条线路称为线路1,下面的称为线路2. 思路 ...

  8. [翻译 EF Core in Action 1.11] 何时不应该使用EF Core

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  9. Android:JNI与NDK(一)

    友情提示:欢迎关注本人公众号,那里有更好的阅读体验以及第一时间获取最新文章 本篇目录 以下举例代码均来自:NDK示例代码 一.前言 安卓开发中很多场景需要用到NDK来开发,比如,音视频的渲染,图像的底 ...

  10. 第8章 动态客户端注册 - IdentityModel 中文文档(v1.0.0)

    OpenID Connect动态客户端注册的客户端库是作为HttpClient扩展方法提供的. 以下代码发送注册请求: var client = new HttpClient(); var respo ...