数据结构与算法简记:红黑树

Love The Way You Lie 2022-09-25 04:28 280阅读 0赞

上次记录了AVL树的相关内容,其规定节点左右子树高度之差不超过1,在添加或移除多个节点后能够对自身重新建立平衡,使其仍可维持一棵良好的二叉查找树结构,不过AVL树为了维护良好的结构,在添加或删除频繁时,性能也会相应的下降。一种替代的方案是使用红黑树。

红黑树(Red Black Tree) 也是一种自平衡二叉查找树,它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。红黑树并不追求AVL树般的严格平衡,减少了平衡所需的操作,从而提高性能。红黑树的应用非常广泛,Java中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的,除此之外,Node.js的定时器管理中也有红黑树的存在。

下面是红黑树需要遵循的规则:

  1. 节点要么是黑色的,要么是红色的
  2. 根节点永远是黑色的
  3. 一条路径上不能有两个相邻的红节点,也就是说,红色节点不能有红色父节点或红色子节点
  4. 从一个节点到叶子节点的每条路径都应该包含相等数量的黑色节点,称为这条路径的黑高度

从某个节点出发,到达一个叶子节点的任意一条路径上,所有的黑色节点的个数成为该节点的黑高度。红黑树的黑高度定义为其根结点的黑高度。为了方便理解,空的叶子节点(NIL)也视为黑色。

下面是一张红黑树的结构图:

20160814120544395

我们构建红黑树的过程是通过逐个插入新节点来完成的,在插入节点时,初始化根节点为黑节点,随后插入子节点时,初始为红色,然后通过修改颜色旋转节点来完成平衡,最终使一棵子树完成平衡操作。

插入新节点时平衡操作如下:

标记新插入的节点为参考节点,如果参考节点的父节点和叔父节点都是红色的,只需将父节点和叔父节点改为黑色,再将祖父节点改为由黑色改为红色(根据上面的第4条可知,如果叔父节点是红色,那么祖父节点必为黑色),然后将祖父节点变为新的参考节点:

20160813150430491

如果参考节点的父节点是红色,而叔父节点是黑色(空节点也视为黑色),我们需要对参考节点的祖父节点进行旋转操作,这个过程跟AVL树相似,几种类型如下:

如果参考节点的父节点是祖父节点的左子节点,则是LR或LL类型。

如果参考节点是其父节点的右子节点,则是LR类型,应对其父节点进行左旋,然后令父节点成为新的参考节点,变成LL类型:

20160813151233104

如果参考节点是其父节点的左子节点,则是LL类型,应对其祖父节点进行右旋,然后交换父节点和祖父节点的颜色,最后父节点成为新的参考节点:

20160813151052885

如果参考节点的父节点是祖父节点的右子节点,则是RL或RR类型。

如果参考节点是其父节点的左子节点,则是RL类型,应对其父节点进行右旋,然后令父节点成为新的参考节点,变成RR类型:

20160813151439870

如果参考节点是其父节点的右子节点,则是RR类型,应对其祖父节点进行左旋,然后交换父节点和祖父节点的颜色,最后父节点成为新的参考节点:

20160813151353932

以上图示参考自:http://www.geeksforgeeks.org/red-black-tree-set-2-insert/

下面是创建红黑树插入节点过程的实现(关于删除节点的图示和实现代码,有时间会再更新):

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define RED 1
  4. #define BLACK 0
  5. //红黑树树节点结构体
  6. typedef struct node {
  7. int data;
  8. struct node *lchild, *rchild, *parent;
  9. int color;
  10. } RBTreeNode;
  11. //红黑树结构体
  12. typedef struct tree {
  13. RBTreeNode *root;
  14. } RBTree;
  15. //初始化红黑树
  16. RBTree * initRBTree();
  17. //插入指定值的节点并保持平衡
  18. void insertNodeWithBalance(RBTree *tree, int key);
  19. //插入一个新节点
  20. RBTreeNode * insertNode(RBTreeNode *root, RBTreeNode *node);
  21. //插入新节点后恢复平衡
  22. void balanceAfterInsertion(RBTree *tree, RBTreeNode *node);
  23. //空节点视为黑色,否则返回节点颜色
  24. int isRed(RBTreeNode *node);
  25. int isBlack(RBTreeNode *node); //!isRed
  26. void swapColor(RBTreeNode *node1, RBTreeNode *node2);
  27. void rotateLeft(RBTree *tree, RBTreeNode *node);
  28. void rotateRight(RBTree *tree, RBTreeNode *node);
  29. void inOrderTraverse(RBTreeNode *node);
  30. void preOrderTraverse(RBTreeNode *node);
  31. int main(int argc, const char * argv[]) {
  32. int array[] = {
  33. 11, 2, 14, 1, 7, 15, 5, 8};
  34. RBTree *tree = initRBTree();
  35. int size = 8;
  36. for (int i = 0; i < size; i++) {
  37. insertNodeWithBalance(tree, array[i]);
  38. }
  39. printf("in: ");
  40. inOrderTraverse(tree->root);
  41. printf("\npre: ");
  42. preOrderTraverse(tree->root);
  43. return 0;
  44. }
  45. //初始化红黑树
  46. RBTree * initRBTree() {
  47. RBTree *tree = (RBTree *) malloc(sizeof(RBTree));
  48. tree->root = NULL;
  49. return tree;
  50. }
  51. //中序遍历
  52. void inOrderTraverse(RBTreeNode *node) {
  53. if (node != NULL) {
  54. inOrderTraverse(node->lchild);
  55. printf("(%d:%c) ", node->data, node->color == RED ? 'R' : 'B');
  56. inOrderTraverse(node->rchild);
  57. }
  58. }
  59. //前序遍历
  60. void preOrderTraverse(RBTreeNode *node) {
  61. if (node != NULL) {
  62. printf("(%d:%c) ", node->data, node->color == RED ? 'R' : 'B');
  63. preOrderTraverse(node->lchild);
  64. preOrderTraverse(node->rchild);
  65. }
  66. }
  67. //插入节点并保持平衡
  68. void insertNodeWithBalance(RBTree *tree, int key) {
  69. RBTreeNode *newNode = (RBTreeNode *) malloc(sizeof(RBTreeNode));
  70. newNode->data = key;
  71. newNode->lchild = newNode->rchild = NULL;
  72. newNode->parent = NULL;
  73. newNode->color = RED;
  74. //插入节点
  75. tree->root = insertNode(tree->root, newNode);
  76. //恢复平衡
  77. balanceAfterInsertion(tree, newNode);
  78. }
  79. //插入节点
  80. RBTreeNode * insertNode(RBTreeNode *root, RBTreeNode *node) {
  81. if (root == NULL) {
  82. return node;
  83. }
  84. if (node->data < root->data) {
  85. root->lchild = insertNode(root->lchild, node);
  86. root->lchild->parent = root;
  87. } else {
  88. root->rchild = insertNode(root->rchild, node);
  89. root->rchild->parent = root;
  90. }
  91. return root;
  92. }
  93. //恢复平衡
  94. void balanceAfterInsertion(RBTree *tree, RBTreeNode *node) {
  95. RBTreeNode *parent = NULL;
  96. RBTreeNode *grandpa = NULL;
  97. //当前节点不是根节点,并且它和父节点同为红色
  98. while (node != tree->root && isRed(node) && isRed(node->parent)) {
  99. parent = node->parent;
  100. grandpa = parent->parent;
  101. /**
  102. * Case : A
  103. * 父节点是祖父节点的左子节点,LR或LL型
  104. **/
  105. if (grandpa->lchild == parent) {
  106. RBTreeNode *uncle = grandpa->rchild;
  107. /**
  108. * Case : 1
  109. * 叔父节点也是红色,只需重新染色
  110. **/
  111. if (isRed(uncle)) {
  112. uncle->color = BLACK;
  113. parent->color = BLACK;
  114. grandpa->color = RED;
  115. //更新当前节点为祖父节点
  116. node = grandpa;
  117. } else {
  118. /**
  119. * Case : 2
  120. * 当前节点是父节点的右子节点,LR型,需要先对父节点左旋,旋转后变为LL型
  121. **/
  122. if (parent->rchild == node) {
  123. //父节点左旋
  124. rotateLeft(tree, parent);
  125. node = parent;
  126. parent = node->parent;
  127. }
  128. /**
  129. * Case : 3
  130. * 当前节点是父节点的左子节点,LL型,需要对祖父节点右旋
  131. **/
  132. rotateRight(tree, grandpa);
  133. //祖父节点右旋后,父节点成为根节点,需交换颜色
  134. swapColor(parent, grandpa);
  135. //更新当前根节点为父节点
  136. node = parent;
  137. }
  138. }
  139. /**
  140. * Case : B
  141. * 父节点是祖父节点的右子节点,RL或RR型
  142. **/
  143. else {
  144. RBTreeNode *uncle = grandpa->lchild;
  145. /**
  146. * Case : 1
  147. * 叔父节点也是红色,只需重新染色
  148. **/
  149. if (isRed(uncle)) {
  150. uncle->color = BLACK;
  151. parent->color = BLACK;
  152. grandpa->color = RED;
  153. //更新当前节点为祖父节点
  154. node = grandpa;
  155. } else {
  156. /**
  157. * Case : 2
  158. * 当前节点是父节点的左子节点,RL型,需要先对父节点右旋,旋转后变为RR型
  159. **/
  160. if (parent->lchild == node) {
  161. rotateRight(tree, parent);
  162. node = parent;
  163. parent = node->parent;
  164. }
  165. /**
  166. * Case : 3
  167. * 当前节点是父节点的右子节点,RR型,需要对祖父节点左旋
  168. **/
  169. rotateLeft(tree, grandpa);
  170. swapColor(parent, grandpa);
  171. node = parent;
  172. }
  173. }
  174. }
  175. //最后须将树的根节点置为黑色
  176. tree->root->color = BLACK;
  177. }
  178. //右旋
  179. void rotateRight(RBTree *tree, RBTreeNode *node) {
  180. RBTreeNode *pivot = node->lchild;
  181. //和轴点右子节点建立关联
  182. node->lchild = pivot->rchild;
  183. if (node->lchild != NULL) {
  184. node->lchild->parent = node;
  185. }
  186. //轴点和原祖父节点建立关联
  187. pivot->parent = node->parent;
  188. if (node->parent != NULL) {
  189. if (node->parent->lchild == node) {
  190. node->parent->lchild = pivot;
  191. } else {
  192. node->parent->rchild = pivot;
  193. }
  194. }
  195. //轴点与原父节点建立关联
  196. pivot->rchild = node;
  197. node->parent = pivot;
  198. if (pivot->parent == NULL) {
  199. tree->root = pivot;
  200. }
  201. }
  202. //左旋
  203. void rotateLeft(RBTree *tree, RBTreeNode *node) {
  204. RBTreeNode *pivot = node->rchild;
  205. //和轴点左子节点建立关联
  206. node->rchild = pivot->lchild;
  207. if (node->rchild != NULL) {
  208. node->parent->rchild = pivot;
  209. }
  210. //轴点和原祖父节点建立关联
  211. pivot->parent = node->parent;
  212. if (node->parent != NULL) {
  213. if (node->parent->lchild == node) {
  214. node->parent->lchild = pivot;
  215. } else {
  216. node->parent->rchild = pivot;
  217. }
  218. }
  219. //轴点和原父节点建立关联
  220. pivot->lchild = node;
  221. node->parent = pivot;
  222. if (pivot->parent == NULL) {
  223. tree->root = pivot;
  224. }
  225. }
  226. //节点是否为红色
  227. int isRed(RBTreeNode *node) {
  228. //空的叶子节点视为黑节点
  229. if (node == NULL) return BLACK;
  230. return node->color;
  231. }
  232. //节点是否为黑色
  233. int isBlack(RBTreeNode *node) {
  234. return !isRed(node);
  235. }
  236. //交换节点颜色
  237. void swapColor(RBTreeNode *node1, RBTreeNode *node2) {
  238. int color = node1->color;
  239. node1->color = node2->color;
  240. node2->color = color;
  241. }

发表评论

表情:
评论列表 (有 0 条评论,280人围观)

还没有评论,来说两句吧...

相关阅读

    相关 数据结构算法 -

    红黑树是一棵自平衡的二叉搜索树,因此在学习红黑树之前,我们需要回顾一下之前所学的知识 二叉搜索树和平衡二叉树。 1、二叉搜索树 二叉搜索树又叫二叉查找树或者二叉排序

    相关 数据结构算法——(上)

    为什么工程中都用红黑树这种二叉树? 上两节,我们依次讲了树、二叉树、二叉查找树。二叉查找树是最常用的一种二叉树,它支持快速插入、删除、查找操作,各个操作的时间复杂度跟树的

    相关 数据结构算法简记:AVL

    前面记录了二叉查找树,它在搜索方面的效率显而易见,可它也存在某种缺陷,假设我们连续插入较小或较大的数据,那么二叉查找树将会逐渐退变为一个线性结构,从而搜索就变为了线性查找,效率

    相关 Java数据结构算法

    概要 概述:R-B Tree,又称为“红黑树”。本文参考了《算法导论》中红黑树相关知识,加之自己的理解,然后以图文的形式对红黑树进行说明。本文的主要内容包括:红黑树的特性

    相关 数据结构

    一. 红黑树的概念 红黑树是一颗二叉搜索树,它的每个结点增加一个存储单位来表示结点的颜色,这个颜色是red或者black,通过对任何一条从根结点到叶子结点上的颜色来约束,

    相关 C++数据结构算法

    > 前情回顾:一棵高度为h的二叉搜索树,它可以支持任何一种基本动态几何操作,如查找、插入、删除等,其时间复杂度为O(h)。因此,如果搜索树的高度较低时,这些集合操作会执行得较快