1. 概述

红⿊树(R-B Tree)是由Rudolf Bayer1972发明。是⼀种⾃平衡⼆叉搜索树,其中每个结点都有⼀个额外的位,存储该结点的颜⾊(红⾊或⿊⾊)

1.1 特性

  • 时间复杂度:插⼊、搜索和删除操作O(logn)

  • 空间复杂度:O(n)

  • 红⿊树还添加了⼀种特殊类型的结点 ,称为NIL结点。NIL结点将做为红⿊树的伪叶⼦结点出现

  • 对于任意⼀个结点n

    • 结点的颜⾊只能是红⾊或者⿊⾊

    • 根结点是⿊⾊的

    • 从根到任何叶结点的每条路径必须具有相同数量的⿊⾊结点

    • 没有两个红⾊结点可以相连,即⼀个红⾊结点不能是另⼀个红⾊结点的⽗结点或⼦结点

    • NIL结点的颜⾊是⿊⾊

    • 如果结点的颜⾊是红⾊,则其⼦结点均为⿊⾊

    • 具有n个结点的红⿊树的⾼度为:h <= 2log(n + 1)

    • 红⿊树的⿊⾊⾼度是从根结点到叶结点的路径上的⿊⾊结点数。叶结点也算作⿊⾊结点。因此,⾼度为h的红⿊树的⿊⾊⾼度 >= h / 2

  • 在每次插⼊或删除后,如果任何结点的平衡因⼦不遵循平衡属性

    • 左旋

    • 右旋

1.2 优势

  • ⼤多数BST操作,例如:搜索、最⼤值、最⼩值、插⼊、删除,都需要O(h)时间,其中hBST的⾼度

  • 如果我们确保每次插⼊和删除后树的⾼度始终为O(log n),那么就可以保证所有这些操作的上限为O(log

n)。红⿊树的⾼度始终为O(log n),其中n是树中的结点数

  • 红⿊树主要⽤于实现频繁执⾏插⼊或删除

  • ⼤多数⾃平衡BST库函数,如C++中的mapset,或Java中的TreeSetTreeMap都使⽤红⿊树

  • MySQL还使⽤红⿊树作为表索引

  • 实现LinuxCPU调度

1.3 对比AVL树

AVL树 VS 红⿊树:

  • 红⿊树是不符合AVL树的平衡条件的,即每个结点的左⼦树和右⼦树的⾼度最多差1的⼆叉查找树

  • 红⿊树是⽤⾮严格的平衡来换取增删结点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决

  • AVL是严格平衡树,因此在增加或者删除结点的时候,根据不同情况,旋转的次数⽐红⿊树要多。所以红⿊树的插⼊效率更⾼

  • AVL在查询频繁的系统⾥⽐红⿊树更⾼效,原因是AVL树的平均⾼度⽐红⿊树更⼩

  • AVL树主要用于搜索,红⿊树主要用于插入和删除

2. 2-3-4树

红黑树是2-3-4树的一种转型,而2-3-4树是四阶的B树Balance Tree

  • 结点存储12或者3key,并且拥有23或者4个孩⼦

  • 所有叶⼦结点都拥有相同的深度

  • 1 / 2log(N + 1 ) ≤ height ≤ log(N + 1)

B树和平衡二叉树稍有不同,B树属于多叉树又名平衡多路查找树(查找路径不只两个),数据库索引技术里大量使用者B树B+树的数据结构

B+树B树的一个升级版,相对于B树来说,B+树更充分的利用了结点的空间,让查询速度更加稳定,其速度完全接近于二分法查找

2.1 形态

两个结点,和⼆叉树⼀样:
image.png

三个结点,有两个key和三个孩⼦:
image.png

四个结点,有三个key和四个孩⼦:
image.png

假设:2-3-4树中每个结点都拥有相同数量的孩⼦结点d
image.png

  • 整个2-3-4树的搜索时间为O(log(d)N)

  • 如果d = N¹⁄² ,那么该树的⾼度为2

  • 2-3-4树可以保证O(logN),如果每个结点只有23或者4个孩⼦

2.2 插⼊

插入元素g,结点从两个变为三个:
image.png

插⼊元素f,结点从三个变为四个:
image.png

插⼊元素e,每当我们到达⼀个4结点,把它分成两个2结点,并将中间元素向上移动进⽗结点
image.png

插⼊元素g,每当我们到达⼀个4结点,把它分成两个2结点,并将中间元素向上移动进⽗结点
image.png

2.3 特性

  • 搜索的时间复杂度是O(log N)

  • 插⼊的时间复杂度是O(log N)

  • 删除的时间复杂度是O(log N)

优点:

  • 平衡

  • O(log N)

缺点:

  • 数据结构复杂

我们将2-3-4树的优点带⼊到⼆叉树中,就是红⿊树。它继承了2-3-4树的优点,将搜索、插入、删除的时间控制在O(log N)

3. 红黑树

2-3-4树的每一个结点都对应红黑树的一种结构,所以每一棵2-3-4树也都对应一棵红黑树

3.1 形态

2-3-4树与红黑树的对应形态:

两个结点,对应红黑树结构:
image.png

三个结点,对应红黑树结构:
image.png

  • ab谁作为根结点,取决于它们的插入顺序,先插入的元素为根结点

四个结点,对应红黑树结构:
image.png

  • 依次插入结点a、b、ca作为根结点,ba的右孩子,cb的右孩子,此时结构为一棵右斜树

  • 根据红黑树特性,根结点a一定为黑色,而bc作为新插入结点为红色

  • 由于红黑树不允许两个红⾊结点相连,故此进行左旋修复

  • 修复后的红黑树b为根结点,a、c分别为b的左、右孩子,同时树结构达到平衡

3.2 特性

  • N:结点总数

  • L:叶⼦数量 = N + 1

  • H:⾼度

  • B:⿊⾊结点的⾼度(从根结点到叶结点的路径上的⿊⾊结点数)

  • 规律1:2 ^ B <= N + 1 <= 4 ^ B

  • 规律2:1/2log(N + 1) <= B <= log(N + 1)

  • 规律3:log(N + 1) <= H <= 2log(N + 1)

4. 结点插入

插入的基本流程:

  • 标准⼆叉搜索树,搜索找到要添加的位置

  • 执⾏替换,颜⾊设置为红⾊

  • 新结点添加叶⼦结点(NIL结点),颜⾊设置为⿊⾊

  • 如果该⽗结点也为红⾊,需要修复

  • 如何修复取决于⽗结点的sibling(兄弟结点)

4.1 思路分析

4.1.1 情况⼀(A)

新结点的⽗结点是⿊⾊结点,什么也不做
image.png

4.1.2 情况⼀(B)

新结点的父结点是红色,但它同时作为根结点,直接将其颜色修改为黑色
image.png

4.1.3 情况二(A)

新结点的⽗结点是红⾊结点,它的叔叔结点是⿊⾊结点,⽗结点处沿插⼊结点的相反⽅向旋转,此时是右旋。⽗结点重新设置为⿊⾊,祖父结点设置为红⾊
image.png

4.1.4 情况二(B)

新结点的⽗结点是红⾊结点,它的叔叔结点是⿊⾊结点,⽗结点处沿插⼊结点的相反⽅向旋转,此时是左旋。⽗结点重新设置为⿊⾊,祖父结点设置为红⾊
image.png

4.1.5 情况二(C)

新结点的⽗结点是红⾊结点,不在⼀边。它的叔叔结点是⿊⾊结点,先左旋,再右旋
image.png

4.1.6 情况二(D)

新结点的⽗结点是红⾊结点,不在⼀边。它的叔叔结点是⿊⾊结点,先右旋,再左旋
image.png

3.2.7 情况三

新结点的⽗结点是红⾊结点,它的叔叔结点是红⾊结点。将⽗结点和叔叔结点重新着⾊为⿊⾊,将祖⽗结点重新着⾊为红⾊。如果祖⽗结点为根结点,重新着⾊为黑⾊
image.png

如果祖⽗结点着⾊为红⾊,有可能还会面临红色相连的结点,例如:R、U
image.png

需要将祖⽗结点进行递归修复
image.png

4.2 代码实现

4.2.1 枚举定义

  1. class RBTree<Element: Comparable> {
  2. enum RBColor : Int {
  3. case Red = 0
  4. case Black = 1
  5. }
  6. enum RBRotating : Int {
  7. case Left = 0
  8. case Right = 1
  9. }
  10. }

4.2.2 结点结构

  1. class RBTree<Element: Comparable> {
  2. fileprivate class RBNode: Equatable {
  3. static func == (lhs: RBTree<Element>.RBNode, rhs: RBTree<Element>.RBNode) -> Bool {
  4. let obj1 = Unmanaged<AnyObject>.passUnretained(lhs).toOpaque();
  5. let obj2 = Unmanaged<AnyObject>.passUnretained(rhs).toOpaque();
  6. return obj1 == obj2;
  7. }
  8. static func != (lhs: RBTree<Element>.RBNode, rhs: RBTree<Element>.RBNode) -> Bool {
  9. let obj1 = Unmanaged<AnyObject>.passUnretained(lhs).toOpaque();
  10. let obj2 = Unmanaged<AnyObject>.passUnretained(rhs).toOpaque();
  11. return obj1 != obj2;
  12. }
  13. // 颜色
  14. var color : RBColor;
  15. // 元素
  16. var data : Element?;
  17. // 左孩子
  18. var leftChild : RBNode?;
  19. // 右孩子
  20. var rightChild : RBNode?;
  21. // 父结点
  22. var parent : RBNode?;
  23. // 初始化
  24. init(data: Element?, parent: RBNode?) {
  25. if(parent == nil) {
  26. // 根结点,默认黑色
  27. self.color = RBColor.Black;
  28. }
  29. else if(data == nil) {
  30. // NIL结点,默认黑色
  31. self.color = RBColor.Black;
  32. }
  33. else {
  34. // 其他新创建的结点,默认红色
  35. self.color = RBColor.Red;
  36. }
  37. self.data = data;
  38. self.parent = parent;
  39. if(data != nil){
  40. self.leftChild = RBNode(data: nil, parent: self);
  41. self.rightChild = RBNode(data: nil, parent: self);
  42. }
  43. }
  44. // 颜色格式化
  45. var colorFormat: String {
  46. if(self.color == RBColor.Red){
  47. return "red";
  48. }
  49. return "black";
  50. }
  51. // 当前结点是否为根结点
  52. var isRoot: Bool {
  53. if(self.parent == nil){
  54. return true;
  55. }
  56. return false;
  57. };
  58. // 当前结点是否为左孩子结点
  59. var isLeftChild: Bool {
  60. get{
  61. if(self.isRoot){
  62. return false;
  63. }
  64. if(self.parent!.leftChild == self) {
  65. return true;
  66. }
  67. return false;
  68. }
  69. };
  70. // 当前结点是否为右孩子结点
  71. var isRightChild: Bool {
  72. get{
  73. if(self.isRoot){
  74. return false;
  75. }
  76. if(self.parent!.rightChild == self) {
  77. return true;
  78. }
  79. return false;
  80. }
  81. };
  82. // 当前结点是否存在父结点
  83. var isExistParent: Bool {
  84. get{
  85. if(self.isRoot){
  86. return false;
  87. }
  88. return true;
  89. }
  90. };
  91. // 当前结点是否存在祖父结点
  92. var isExistGrandparent: Bool {
  93. get{
  94. if(!self.isExistParent){
  95. return false;
  96. }
  97. return self.parent!.isExistParent;
  98. }
  99. };
  100. // 获取祖父结点
  101. var grandparent: RBNode? {
  102. if(!self.isExistGrandparent){
  103. return nil;
  104. }
  105. return self.parent?.parent;
  106. };
  107. // 当前结点是否为叶子结点
  108. var isLeaf: Bool {
  109. get{
  110. if(self.leftChild == nil && self.rightChild == nil){
  111. return true;
  112. }
  113. return false;
  114. }
  115. };
  116. // 当前结点是否NIL结点
  117. var isNilLeaf: Bool {
  118. get{
  119. if(self.data == nil && self.color == RBColor.Black && self.isLeaf){
  120. return true;
  121. }
  122. return false;
  123. }
  124. };
  125. // 是否存在兄弟结点
  126. var isExistSibling: Bool {
  127. get{
  128. if(self.isRoot){
  129. return false;
  130. }
  131. return true;
  132. }
  133. };
  134. // 获取兄弟结点
  135. var sibling: RBNode? {
  136. get{
  137. if(!self.isExistSibling){
  138. return nil;
  139. }
  140. if(self.isLeftChild){
  141. return self.parent!.rightChild;
  142. }
  143. return self.parent!.leftChild;
  144. }
  145. };
  146. // 是否存在叔叔结点
  147. var isExistUncle: Bool {
  148. get{
  149. if(self.isRoot){
  150. return false;
  151. }
  152. if(self.parent!.isRoot){
  153. return false;
  154. }
  155. return true;
  156. }
  157. };
  158. // 获取叔叔结点
  159. var uncle: RBNode? {
  160. if(!self.isExistUncle){
  161. return nil;
  162. }
  163. return self.parent!.sibling;
  164. }
  165. }
  166. }

4.2.3 添加元素

  1. class RBTree<Element: Comparable> {
  2. fileprivate var _root: RBNode?;
  3. // 添加元素
  4. func add(data: Element) {
  5. // 添加,返回新结点
  6. let node = addNode(data: data);
  7. // 新结点为空,添加失败,直接返回
  8. if(node == nil){
  9. return;
  10. }
  11. // 修复
  12. addFixup(node: node!);
  13. }
  14. // 添加结点
  15. fileprivate func addNode(data: Element) -> RBNode? {
  16. // 根结点为空
  17. if(_root == nil){
  18. // 创建根结点
  19. _root = RBNode(data: data, parent: nil);
  20. return _root!;
  21. }
  22. var node = _root!;
  23. while(!node.isNilLeaf) {
  24. if(data < node.data!){
  25. if(node.leftChild!.isNilLeaf) {
  26. node.leftChild = RBNode(data: data, parent: node);
  27. return node.leftChild!;
  28. }
  29. node = node.leftChild!;
  30. continue;
  31. }
  32. if(node.rightChild!.isNilLeaf) {
  33. node.rightChild = RBNode(data: data, parent: node);
  34. return node.rightChild!;
  35. }
  36. node = node.rightChild!;
  37. }
  38. return nil;
  39. }
  40. }

4.2.4 结点修复

  1. class RBTree<Element: Comparable> {
  2. // 修复
  3. fileprivate func addFixup(node: RBNode) {
  4. // 当前结点为根结点
  5. if(node.isRoot) {
  6. // 着色为黑色
  7. node.color = RBColor.Black;
  8. // 直接返回
  9. return;
  10. }
  11. // 没有父结点,直接返回
  12. if(!node.isExistParent) {
  13. return;
  14. }
  15. // 获取父结点
  16. let parent = node.parent!;
  17. // 【情况⼀(A)】新结点的⽗结点是⿊⾊结点,什么也不做
  18. if(parent.color == RBColor.Black) {
  19. return;
  20. }
  21. // 【情况⼀(B)】新结点的父结点是红色,但它同时作为根结点,直接将其颜色修改为黑色
  22. if(parent.color == RBColor.Red && parent.isRoot){
  23. parent.color = RBColor.Black;
  24. return;
  25. }
  26. // 没有叔叔结点,或没有祖父结点,直接返回
  27. if(!node.isExistUncle || !node.isExistGrandparent) {
  28. return;
  29. }
  30. // 获取叔叔结点
  31. let uncle = node.uncle!;
  32. // 获取祖父结点
  33. let grandparent = node.grandparent!;
  34. // 【情况二】新结点的⽗结点是红⾊结点,它的叔叔结点是⿊⾊结点
  35. if(parent.color == RBColor.Red && uncle.color == RBColor.Black){
  36. // 保存当前结点
  37. var currNode = node;
  38. // 【情况二(C)】不在⼀边,先左旋
  39. if(parent.isLeftChild && currNode.isRightChild) {
  40. // 左旋
  41. rotating(node: parent, rbRotating: RBRotating.Left);
  42. // 旋转后当前结点改变,更新当前结点
  43. currNode = parent;
  44. }
  45. // 【情况二(D)】不在⼀边,先右旋
  46. else if(parent.isRightChild && currNode.isLeftChild) {
  47. // 右旋
  48. rotating(node: parent, rbRotating: RBRotating.Right);
  49. // 旋转后当前结点改变,更新当前结点
  50. currNode = parent;
  51. }
  52. // ⽗结点重新设置为⿊⾊
  53. currNode.parent!.color = RBColor.Black;
  54. // 祖父结点设置为红⾊
  55. currNode.grandparent!.color = RBColor.Red;
  56. // 【情况二(A)】父结点与新结点在同一边,⽗结点处沿插⼊结点的相反⽅向旋转,此时是右旋
  57. if(currNode.isLeftChild) {
  58. rotating(node: currNode.grandparent!, rbRotating: RBRotating.Right);
  59. }
  60. // 【情况二(B)】父结点与新结点在同一边,⽗结点处沿插⼊结点的相反⽅向旋转,此时是左旋
  61. else {
  62. rotating(node: currNode.grandparent!, rbRotating: RBRotating.Left);
  63. }
  64. return;
  65. }
  66. // 【情况三】新结点的⽗结点是红⾊结点,它的叔叔结点是红⾊结点。将⽗结点和叔结点重新着⾊为⿊⾊,将祖⽗结点重新着⾊为红⾊
  67. if(parent.color == RBColor.Red && uncle.color == RBColor.Red) {
  68. // 将⽗结点重新着⾊为⿊⾊
  69. parent.color = RBColor.Black;
  70. // 将叔叔结点重新着⾊为⿊⾊
  71. uncle.color = RBColor.Black;
  72. // 将祖⽗结点重新着⾊为红⾊
  73. grandparent.color = RBColor.Red;
  74. // 如果祖⽗结点着⾊为红⾊,有可能还会面临红色相连的结点,传入祖⽗结点进行递归修复
  75. addFixup(node: grandparent);
  76. }
  77. }
  78. }

4.2.5 结点旋转

  1. class RBTree<Element: Comparable> {
  2. // 旋转
  3. fileprivate func rotating(node: RBNode, rbRotating: RBRotating) {
  4. // 左旋
  5. if(rbRotating == RBRotating.Left){
  6. // 获取当前结点的右孩子
  7. let rightNode = node.rightChild;
  8. // 当前结点为根结点
  9. if(node.isRoot){
  10. // 将根结点,赋值为当前结点的右孩子
  11. _root = rightNode;
  12. // 当前结点右孩子的父结点,赋值为nil
  13. rightNode?.parent = nil;
  14. }
  15. else {
  16. // 获取当前结点的父结点
  17. let parent = node.parent;
  18. // 判断当前结点是左孩子、还是右孩子
  19. if(node.isLeftChild){
  20. // 当前结点为左孩子,将父结点的左孩子,赋值为当前结点的右孩子
  21. parent?.leftChild = rightNode;
  22. }
  23. else{
  24. // 当前结点为右孩子,将父结点的右孩子,赋值为当前结点的右孩子
  25. parent?.rightChild = rightNode;
  26. }
  27. // 当前结点右孩子的父结点,赋值为父结点
  28. rightNode?.parent = parent;
  29. }
  30. // 获取当前结点右孩子的左孩子
  31. let rightLeftNode = rightNode?.leftChild;
  32. // 当前结点的右孩子,赋值为当前结点右孩子的左孩子
  33. node.rightChild = rightLeftNode;
  34. // 当前结点右孩子的左孩子的父结点,赋值为当前结点
  35. rightLeftNode?.parent = node;
  36. // 当前结点右孩子的左孩子,赋值为当前结点
  37. rightNode?.leftChild = node;
  38. // 当前结点的父结点,赋值为当前结点的右孩子
  39. node.parent = rightNode;
  40. }
  41. // 右旋
  42. else {
  43. // 获取当前结点的左孩子
  44. let leftNode = node.leftChild;
  45. // 当前结点为根结点
  46. if(node.isRoot){
  47. // 将根结点,赋值为当前结点的左孩子
  48. _root = leftNode;
  49. // 当前结点左孩子的父结点,赋值为nil
  50. leftNode?.parent = nil;
  51. }
  52. else {
  53. // 获取当前结点的父结点
  54. let parent = node.parent;
  55. // 判断当前结点是左孩子、还是右孩子
  56. if(node.isLeftChild){
  57. // 当前结点为左孩子,将父结点的左孩子,赋值为当前结点的左孩子
  58. parent?.leftChild = leftNode;
  59. }
  60. else{
  61. // 当前结点为右孩子,将父结点的右孩子,赋值为当前结点的左孩子
  62. parent?.rightChild = leftNode;
  63. }
  64. // 当前结点左孩子的父结点,赋值为父结点
  65. leftNode?.parent = parent;
  66. }
  67. // 获取当前结点左孩子的右孩子
  68. let leftRightNode = leftNode?.rightChild;
  69. // 当前结点的左孩子,赋值为当前结点左孩子的右孩子
  70. node.leftChild = leftRightNode;
  71. // 当前结点左孩子的右孩子的父结点,赋值为当前结点
  72. leftRightNode?.parent = node;
  73. // 当前结点左孩子的右孩子,赋值为当前结点
  74. leftNode?.rightChild = node;
  75. // 当前结点的父结点,赋值为当前结点的左孩子
  76. node.parent = leftNode;
  77. }
  78. }
  79. }

4.2.6 红黑树打印

  1. class RBTree<Element: Comparable> {
  2. func traverse() {
  3. if(_root == nil){
  4. print("[]");
  5. return;
  6. }
  7. let str = traverse(node: _root!, top: "", root: "", bottom: "");
  8. print("\(str)");
  9. }
  10. fileprivate func traverse(node: RBNode, top: String, root: String, bottom: String) -> String {
  11. if (node.isNilLeaf) {
  12. return "\(root)nil【\(node.colorFormat)】\n";
  13. }
  14. if (node.isLeaf) {
  15. return "\(root)nil【\(node.colorFormat)】\n";
  16. }
  17. let ri = traverse(node: node.rightChild!, top: "\(top) ", root: "\(top)┌───", bottom: "\(top)│ ");
  18. let mi = "\(root)\(node.data!)【\(node.colorFormat)】\n";
  19. let li = traverse(node: node.leftChild!, top: "\(bottom)│ ", root: "\(bottom)└───", bottom: "\(bottom) ");
  20. return "\(ri)\(mi)\(li)";
  21. }
  22. }

5. 结点删除

普通二叉搜索树的删除操作:查找当前结点最近的前驱或者后继结点,进行替换

  • 前驱结点:对⼀棵⼆叉树进⾏中序遍历,遍历后的顺序,当前结点的前⼀个结点为该节点的前驱结点。左⼦树的最右结点

  • 后继节点:对⼀棵⼆叉树进⾏中序遍历,遍历后的顺序,当前结点的后⼀个节点为该结点的后继结点。右⼦树的最左结点

示例:删除结点7
image.png

  • 中序遍历:2,4,5,7,8,9

  • 选择最近的前驱结点5进行替换

红黑树的删除操作:

  • 删除结点为红⾊,正常删除,执行BST的删除逻辑即可

  • 删除结点为⿊⾊,⼦结点为红⾊,正常删除,执行BST的删除逻辑即可

  • 删除结点为⿊⾊,⼦结点也为⿊⾊,先执行BST的删除逻辑,再将当前位置的结点变为double black,缩写DB

5.1 思路分析

DB结点可以理解为一个占位结点,针对DB结点进行不同情况的后续处理。它的主要工作,就是将这个double black转换为single black

5.1.1 情况一

如果DB结点为根结点,或者DB结点的颜色为红色

  • DB节点变为正常节点,把颜⾊变为⿊⾊

5.1.2 情况二(A)

如果DB兄弟是⿊⾊的,并且DB兄弟孩⼦中的最远⼀个为红⾊
image.png

  • 交换DB⽗节点的颜⾊与DB兄弟的颜⾊

  • DB⽗节点向DB⽅向进⾏旋转

  • DB节点变为正常节点,把颜⾊变为⿊⾊

  • DB兄弟离DB最远的红⾊孩⼦节点变为⿊⾊

5.1.3 情况二(B)

如果兄弟是⿊⾊的,并且兄弟孩⼦中的最远⼀个为⿊⾊,最近的为红⾊
image.png

  • 交换DB兄弟节点和DB兄弟节点中红⾊孩⼦节点的颜⾊

  • DB兄弟节点向DB反⽅向进⾏旋转

  • 此时DB兄弟孩⼦中的最远⼀个为红⾊,按情况一(A)进行处理

5.1.4 情况二(C)

如果兄弟是⿊⾊的,并且兄弟孩⼦节点颜⾊为⿊⾊,DB⽗节点是红⾊
image.png

  • DB节点变为正常节点,把颜⾊变为⿊⾊

  • DB的兄弟节点变为红⾊

  • DB⽗节点是红⾊,将其变为⿊⾊

5.1.5 情况二(D)

如果兄弟是⿊⾊的,并且兄弟孩⼦节点颜⾊为⿊⾊,DB⽗节点是⿊⾊
image.png

  • DB节点变为正常节点,把颜⾊变为⿊⾊

  • DB的兄弟节点变为红⾊

  • 如果DB的⽗节点是⿊⾊,将其变为DB节点,继续进行祖⽗节点、兄弟结点、兄弟孩子结点的综合判断。满⾜哪种情况,进行相应的后续处理

5.1.6 情况三

如果DB兄弟是红⾊节点
image.png

  • 交换DB⽗节点与DB兄弟节点颜⾊

  • DB⽗节点向DB⽅向旋转

  • 观察此时满⾜哪种情况,进行相应的后续处理

5.2 代码实现

5.2.1 获取兄远 & 兄近

  1. class RBTree<Element: Comparable> {
  2. fileprivate class RBNode: Equatable {
  3. // 获取兄弟孩⼦中的最远⼀个
  4. var siblingChildFar: RBNode? {
  5. if(self.sibling!.isNilLeaf) {
  6. return nil;
  7. }
  8. if(self.isLeftChild) {
  9. return self.sibling!.rightChild;
  10. }
  11. return self.sibling!.leftChild;
  12. }
  13. // 获取兄弟孩⼦中的最近⼀个
  14. var siblingChildNear: RBNode? {
  15. if(self.sibling!.isNilLeaf) {
  16. return nil;
  17. }
  18. if(self.isLeftChild) {
  19. return self.sibling!.leftChild;
  20. }
  21. return self.sibling!.rightChild;
  22. }
  23. }
  24. }

5.2.2 查找元素

  1. class RBTree<Element: Comparable> {
  2. // 找到指定元素所对应的结点
  3. fileprivate func search(data: Element) -> RBNode? {
  4. if(_root == nil) {
  5. return nil;
  6. }
  7. var node = _root!;
  8. while(!node.isNilLeaf) {
  9. if(node.data! == data) {
  10. return node;
  11. }
  12. if(data > node.data!) {
  13. node = node.rightChild!;
  14. continue;
  15. }
  16. node = node.leftChild!;
  17. }
  18. return nil;
  19. }
  20. }

5.2.3 删除元素

  1. class RBTree<Element: Comparable> {
  2. // 删除元素
  3. func delete(data: Element) {
  4. // 寻找待删除元素
  5. let node = search(data: data);
  6. // 未找到
  7. if(node == nil) {
  8. // 直接返回
  9. return;
  10. }
  11. // 删除结点
  12. var delNode: RBNode?;
  13. // DB结点
  14. var dbNode: RBNode?;
  15. // 如果待删除结点的左、右孩子都是NIL结点,或者其中一边为NIL结点
  16. if(node!.leftChild!.isNilLeaf || node!.rightChild!.isNilLeaf) {
  17. // 当前为根结点,并且左、右孩子都是NIL结点
  18. if(node!.isRoot && node!.leftChild!.isNilLeaf && node!.rightChild!.isNilLeaf) {
  19. // 直接将根结点设置为空
  20. _root = nil;
  21. // 直接返回
  22. return;
  23. }
  24. // 寻找目标结点,如果左孩子是NIL结点,目标结点选择右孩子,否则选择左孩子
  25. let target = node!.leftChild!.isNilLeaf ? node!.rightChild! : node!.leftChild!;
  26. // 如果当前结点为根结点
  27. if(node!.isRoot) {
  28. // 将根结点赋值为目标结点
  29. _root = target;
  30. // 将目标结点的父节点,设置为空
  31. target.parent = nil;
  32. }
  33. else {
  34. // 获取待删除结点的父节点
  35. let parent = node!.parent!;
  36. // 判断当前结点为左孩子
  37. if(node!.isLeftChild) {
  38. // 将父节点的左孩子,赋值为目标结点
  39. parent.leftChild = target;
  40. }
  41. else {
  42. // 将父节点的右孩子,赋值为目标结点
  43. parent.rightChild = target;
  44. }
  45. // 将目标结点的父节点,设置为待删除结点的父节点
  46. target.parent = parent;
  47. }
  48. // 删除结点为传入结点
  49. delNode = node!;
  50. // DB结点为目标结点
  51. dbNode = target;
  52. }
  53. // 否则,左、右孩子都非NIL结点
  54. else {
  55. // 获取待删除结点的左子树
  56. var target = node!.leftChild!;
  57. // 找到直接前驱结点
  58. while(!target.rightChild!.isNilLeaf) {
  59. target = target.rightChild!;
  60. }
  61. // 将待删除结点与直接前驱结点的值进行替换
  62. node!.data = target.data;
  63. // 获取直接前驱结点的左孩子
  64. let targetLeft = target.leftChild!;
  65. // 如果直接前驱结点的父节点就是待删除结点
  66. if(target.parent == node) {
  67. // 将待删除结点的左孩子,赋值为直接前驱结点的左孩子
  68. node!.leftChild = targetLeft;
  69. // 将直接前驱结点左孩子的父节点,赋值为待删除结点
  70. targetLeft.parent = node;
  71. }
  72. else {
  73. // 获取直接前驱结点的父节点
  74. let targetParent = target.parent!;
  75. // 将父节点的右孩子,赋值为直接前驱结点的左孩子
  76. targetParent.rightChild = targetLeft;
  77. // 将直接前驱结点左孩子的父节点,赋值为父节点
  78. targetLeft.parent = targetParent;
  79. }
  80. // 删除结点为直接前驱结点
  81. delNode = target;
  82. // DB结点为直接前驱结点的左孩子
  83. dbNode = targetLeft;
  84. }
  85. // 删除结点为红⾊,正常删除,执行BST的删除逻辑即可
  86. if(delNode!.color == RBColor.Red) {
  87. return;
  88. }
  89. // 删除结点为⿊⾊,⼦结点为红⾊,正常删除,执行BST的删除逻辑即可
  90. if(delNode!.leftChild!.color == RBColor.Red && delNode!.rightChild!.color == RBColor.Red) {
  91. return;
  92. }
  93. // 删除结点为⿊⾊,⼦结点也为⿊⾊,先执行BST的删除逻辑,再将当前位置的结点变为double black,缩写DB
  94. deleteFixup(dbNode: dbNode!);
  95. }
  96. }

5.2.4 结点修复

  1. class RBTree<Element: Comparable> {
  2. // 修复
  3. fileprivate func deleteFixup(dbNode: RBNode) {
  4. // 情况一:如果DB结点为根结点,或者DB结点的颜色为红色
  5. if(dbNode.isRoot || dbNode.color == RBColor.Red) {
  6. // 将DB节点变为正常节点,把颜⾊变为⿊⾊
  7. dbNode.color = RBColor.Black;
  8. return;
  9. }
  10. if(!dbNode.isExistParent || !dbNode.isExistSibling){
  11. return;
  12. }
  13. // 情况三:如果DB兄弟是红⾊节点
  14. if(dbNode.sibling!.color == RBColor.Red) {
  15. // 交换DB⽗节点与DB兄弟节点颜⾊
  16. let tempColor = dbNode.parent!.color;
  17. dbNode.parent!.color = dbNode.sibling!.color;
  18. dbNode.sibling!.color = tempColor;
  19. // 获取父节点
  20. let parent = dbNode.parent!;
  21. // DB⽗节点向DB⽅向旋转
  22. if(dbNode.isLeftChild) {
  23. rotating(node: parent, rbRotating: RBRotating.Left);
  24. }
  25. else {
  26. rotating(node: parent, rbRotating: RBRotating.Right);
  27. }
  28. // 观察此时满⾜哪种情况,进行相应的后续处理
  29. deleteFixup(dbNode: dbNode);
  30. return;
  31. }
  32. if(dbNode.sibling!.isNilLeaf) {
  33. return;
  34. }
  35. // 情况二(A):如果DB兄弟是⿊⾊的,并且DB兄弟孩⼦中的最远⼀个为红⾊
  36. if(dbNode.siblingChildFar!.color == RBColor.Red) {
  37. // 交换DB⽗节点的颜⾊与DB兄弟的颜⾊
  38. let tempColor = dbNode.parent!.color;
  39. dbNode.parent!.color = dbNode.sibling!.color;
  40. dbNode.sibling!.color = tempColor;
  41. // 将DB节点变为正常节点,把颜⾊变为⿊⾊
  42. dbNode.color = RBColor.Black;
  43. // 将DB兄弟离DB最远的红⾊孩⼦节点变为⿊⾊
  44. dbNode.siblingChildFar!.color = RBColor.Black;
  45. // 获取父节点
  46. let parent = dbNode.parent!;
  47. // DB⽗节点向DB⽅向进⾏旋转
  48. if(dbNode.isLeftChild) {
  49. rotating(node: parent, rbRotating: RBRotating.Left);
  50. }
  51. else {
  52. rotating(node: parent, rbRotating: RBRotating.Right);
  53. }
  54. return;
  55. }
  56. // 情况二(B):如果兄弟是⿊⾊的,并且兄弟孩⼦中的最远⼀个为⿊⾊,最近的为红⾊
  57. if(dbNode.siblingChildNear!.color == RBColor.Red) {
  58. // 交换DB兄弟节点和DB兄弟节点中红⾊孩⼦节点的颜⾊
  59. let tempColor = dbNode.sibling!.color;
  60. dbNode.sibling!.color = dbNode.siblingChildNear!.color;
  61. dbNode.siblingChildNear!.color = tempColor;
  62. // 获取兄弟结点
  63. let sibling = dbNode.sibling!;
  64. // DB兄弟节点向DB反⽅向进⾏旋转
  65. if(dbNode.isLeftChild) {
  66. rotating(node: sibling, rbRotating: RBRotating.Right);
  67. }
  68. else {
  69. rotating(node: sibling, rbRotating: RBRotating.Left);
  70. }
  71. // 此时DB兄弟孩⼦中的最远⼀个为红⾊,按情况一(A)进行处理
  72. deleteFixup(dbNode: dbNode);
  73. return;
  74. }
  75. // 情况二(C):如果兄弟是⿊⾊的,并且兄弟孩⼦节点颜⾊为⿊⾊,DB⽗节点是红⾊
  76. if(dbNode.parent!.color == RBColor.Red) {
  77. // 将DB节点变为正常节点,把颜⾊变为⿊⾊
  78. dbNode.color = RBColor.Black;
  79. // 将DB的兄弟节点变为红⾊
  80. dbNode.sibling!.color = RBColor.Red;
  81. // DB⽗节点是红⾊,将其变为⿊⾊
  82. dbNode.parent!.color = RBColor.Black;
  83. return;
  84. }
  85. // 情况二(D):如果兄弟是⿊⾊的,并且兄弟孩⼦节点颜⾊为⿊⾊,DB⽗节点是⿊⾊
  86. // 将DB节点变为正常节点,把颜⾊变为⿊⾊
  87. dbNode.color = RBColor.Black;
  88. // 将DB的兄弟节点变为红⾊
  89. dbNode.sibling!.color = RBColor.Red;
  90. // 如果DB的⽗节点是⿊⾊,将其变为DB节点,继续进行祖⽗节点、兄弟结点、兄弟孩子结点的综合判断。满⾜哪种情况,进行相应的后续处理
  91. deleteFixup(dbNode: dbNode.parent!);
  92. }
  93. }

总结

概述:

  • 红⿊树(R-B Tree)是由Rudolf Bayer1972发明。是⼀种⾃平衡⼆叉搜索树,其中每个结点都有⼀个额外的位,存储该结点的颜⾊(红⾊或⿊⾊)

  • 2-3-4树的变种,2-3-4树的每一个结点都对应红黑树的一种结构,所以每一棵2-3-4树也都对应一棵红黑树

结点插入:

  • 标准⼆叉搜索树,搜索找到要添加的位置

  • 执⾏替换,颜⾊设置为红⾊

  • 新结点添加叶⼦结点(NIL结点),颜⾊设置为⿊⾊

  • 如果该⽗结点也为红⾊,需要修复

  • 如何修复取决于⽗结点的sibling(兄弟结点)

结点删除:

  • 删除结点为红⾊,正常删除,执行BST的删除逻辑即可

  • 删除结点为⿊⾊,⼦结点为红⾊,正常删除,执行BST的删除逻辑即可

  • 删除结点为⿊⾊,⼦结点也为⿊⾊,先执行BST的删除逻辑,再将当前位置的结点变为double black,缩写DB

  • DB结点可以理解为一个占位结点,针对DB结点进行不同情况的后续处理。它的主要工作,就是将这个double black转换为single black

学习资料:

数据结构可视化:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

Swift算法俱乐部:https://aquarchitect.github.io/swift-algorithm-club/