当前位置:   article > 正文

红黑树的底层结构实现_红黑树的底层实现

红黑树的底层实现

红黑树的定义

红黑树是含有红黑链接并满足以下条件的二叉查找树

1、红链接均为左连接;

2、没有任何一个结点同时和两条红链接相连;

3、该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。

 红黑树中存储元素的结点

因为每个结点都只会有一条指向自己的链接(即它的父结点指向它),我们可以在之前的Node结点中新增一个布尔类型的变量color来表示此链接的颜色。我们规定:如果指向此结点的链接是红色的,那么该变量的值为true;如果指向此结点的链接时黑色的,那么该变量的值为false。例如下图所示,c是当前结点,h指的是它的父结点,所以h的左子结点(即c结点)的color的值是true

 结点API设计:

 代码实现:

  1. /**
  2. * 结点类
  3. */
  4. private class Node {
  5. /**
  6. * 存储键
  7. */
  8. private Key key;
  9. //存储值
  10. private Value value;
  11. //记录左子结点
  12. private Node left;
  13. //记录右子结点
  14. private Node right;
  15. //标记父结点指向当前结点的链接是否是红色:true表示是红色,false表示不是红色(即黑色)
  16. private boolean color;
  17. /**
  18. * 构造方法
  19. */
  20. public Node(Key key, Value value, Node left, Node right, boolean color) {
  21. this.key = key;
  22. this.value = value;
  23. this.left = left;
  24. this.right = right;
  25. this.color = color;
  26. }
  27. }

平衡化

在对红黑树进行一些增删改的操作后,很有可能会出现红色的右链接或者两条连续的红色的链接,而这些都不满足红黑树的定义,所以我们需要对这些情况通过旋转进行修复,让红黑树保持平衡。

左旋

当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。

例如:当前结点为h,它的右子结点为x;

左旋过程:

1、让x的左子结点变为h的右子结点:h.right=x.left;

2、让h成为x的左子结点:x.left=h;

3、让h的color属性变为x的color属性值:x.color=h.color;

4、让h的color属性变为RED:h.color=true;

右旋

当某个结点的左子结点为红色,左子结点的左子结点也为红色,此时需要右旋。

例如:当前结点为h,它的左子结点为x;

右旋过程:

1、让x的右子结点变为h的左子结点:h.left=x.right;

2、让h成为x的右子结点:x.right=h;

3、让h的color属性变为x的color属性值:x.color=h.color;

4、让h的color属性变为RED:h.color=true;

颜色反转

当一个结点的左子结点和右子结点的color都为RED时,此时只需要把左子结点和右子结点的颜色变为BLACK,同时让当前结点的颜色变为RED即可。

根结点的颜色总是黑色

在结点Node对象中color属性表示的是父结点指向当前结点的连接的颜色,由于根结点不存在父结点,所以每次插入操作后,我们都需要把根结点的颜色设置为黑色。

红黑树的API设计

 红黑树代码实现

  1. /**
  2. * @author: xuzhilei
  3. * @create: 2021-12-21
  4. * @description: 红黑树
  5. **/
  6. public class RedBlackTree<Key extends Comparable<Key>, Value> {
  7. /**
  8. * 根结点
  9. */
  10. private Node root;
  11. /**
  12. * 记录树中的元素个数
  13. */
  14. private int number;
  15. /**
  16. * 表示红色链接
  17. */
  18. private static final boolean RED = true;
  19. /**
  20. * 表示黑色链接
  21. */
  22. private static final boolean BLACK = false;
  23. /**
  24. * 结点类
  25. */
  26. private class Node {
  27. /**
  28. * 存储键
  29. */
  30. private Key key;
  31. //存储值
  32. private Value value;
  33. //记录左子结点
  34. private Node left;
  35. //记录右子结点
  36. private Node right;
  37. //标记父结点指向当前结点的链接是否是红色:true表示是红色,false表示不是红色(即黑色)
  38. private boolean color;
  39. /**
  40. * 构造方法
  41. */
  42. public Node(Key key, Value value, Node left, Node right, boolean color) {
  43. this.key = key;
  44. this.value = value;
  45. this.left = left;
  46. this.right = right;
  47. this.color = color;
  48. }
  49. }
  50. /**
  51. *构造方法
  52. */
  53. public RedBlackTree() {
  54. this.root = null;
  55. this.number = 0;
  56. }
  57. /**
  58. * 获取红黑树中的元素个数
  59. *
  60. * @return int
  61. */
  62. public int size() {
  63. return number;
  64. }
  65. /**
  66. * 判断当前结点x的父结点指向它的链接是否为红色
  67. *
  68. * @param x 当前结点
  69. * @return boolean
  70. */
  71. private boolean isRed(Node x) {
  72. if (x == null) {
  73. return false;
  74. }
  75. return x.color == RED;
  76. }
  77. /**
  78. * 左旋转
  79. * 当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
  80. * 前提:当前结点为h,它的右子结点为x;
  81. * 左旋过程:
  82. * 1.让x的左子结点变为h的右子结点:h.right=x.left;
  83. * 2.让h成为x的左子结点:x.left=h;
  84. * 3.让h的color属性变为x的color属性值:x.color=h.color;
  85. * 4.让h的color属性变为RED:h.color=true;
  86. *
  87. * @param h 当前结点
  88. * @return h的右子结点x
  89. */
  90. private Node rotateLeft(Node h) {
  91. //获取h结点的右子结点x
  92. Node x = h.right;
  93. //找到x结点的左子结点,让x结点的左子结点成为h结点的右子结点
  94. h.right = x.left;
  95. //让h成为x的左子结点
  96. x.left = h;
  97. //让h的color属性变为x的color属性值
  98. x.color = h.color;
  99. //让h的color属性变为RED
  100. h.color = RED;
  101. //返回x结点
  102. return x;
  103. }
  104. /**
  105. * 右旋转
  106. * 当某个结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋
  107. * 前提:当前结点为h,它的左子结点为x;
  108. * 右旋过程:
  109. * 1. 让x的右子结点成为h的左子结点:h.left = x.right;
  110. * 2. 让h成为x的右子结点:x.right=h;
  111. * 3. 让x的color变为h的color属性值:x.color = h.color;
  112. * 4. 让h的color为RED;
  113. *
  114. * @param h 当前结点
  115. * @return h的左子结点x
  116. */
  117. private Node rotateRight(Node h) {
  118. //获取h结点的左子结点x
  119. Node x = h.left;
  120. //找到x结点的右子结点,让x结点的右子结点成为h结点的左子结点
  121. h.left = x.right;
  122. //让h成为x的右子结点
  123. x.right = h;
  124. //让x的color变为h的color属性值
  125. x.color = h.color;
  126. //让h的color属性值变为RED
  127. h.color = RED;
  128. //返回x结点
  129. return x;
  130. }
  131. /**
  132. * 颜色反转
  133. * 当某个结点的左子结点是红色,且右子结点也是红色,需要颜色反转
  134. *
  135. * @param h 当前结点
  136. */
  137. private void flipColors(Node h) {
  138. //当前结点的color属性值变为RED
  139. h.color = RED;
  140. //左子结点和右子结点的color属性都变为BLACK
  141. h.left.color = BLACK;
  142. h.right.color = BLACK;
  143. }
  144. /**
  145. * 向整个树中插入键值对key-value
  146. *
  147. * @param key 待插入键
  148. * @param value 待插入值
  149. */
  150. public void put(Key key, Value value) {
  151. root = put(this.root, key, value);
  152. //根结点的color属性总是黑色
  153. root.color = BLACK;
  154. }
  155. /**
  156. * 往指定树h中插入键值对key-value,并返回插入元素后的新树
  157. *
  158. * @param h 指定树的根结点
  159. * @param key 待插入键
  160. * @param value 待插入值
  161. */
  162. private Node put(Node h, Key key, Value value) {
  163. //如果当前结点为空,则直接创建一个结点并返回此结点
  164. if (h == null) {
  165. //元素个数+1
  166. number++;
  167. return new Node(key, value, null, null, RED);
  168. }
  169. //比较结点h的键和参数key的大小
  170. int comp = key.compareTo(h.key);
  171. if (comp < 0) {
  172. //key比当前结点h的键要小,继续向h的左子结点所在的树进行插入操作
  173. h.left = put(h.left, key, value);
  174. } else if (comp > 0) {
  175. //key比当前结点h的键要大,继续向h的右子结点所在的树进行插入操作
  176. h.right = put(h.right, key, value);
  177. } else {
  178. //key与当前结点h的键相等,则用待插入的value替换当前结点的value值
  179. h.value = value;
  180. }
  181. //满足左旋转条件:当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
  182. if (isRed(h.right) && !isRed(h.left)) {
  183. h = rotateLeft(h);
  184. }
  185. //满足右旋转条件:当某个结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋
  186. if (isRed(h.left) && isRed(h.left.left)) {
  187. h = rotateRight(h);
  188. }
  189. //满足颜色反转:当某个结点的左子结点是红色,且右子结点也是红色,需要颜色反转
  190. if (isRed(h.left) && isRed(h.right)) {
  191. flipColors(h);
  192. }
  193. return h;
  194. }
  195. /**
  196. * 根据key在整个树中找出对应的value
  197. *
  198. * @param key 指定的key
  199. * @return Value 对应的value
  200. */
  201. public Value get(Key key) {
  202. return get(root, key);
  203. }
  204. /**
  205. * 根据key在指定树x中找出对应的value
  206. *
  207. * @param x 指定树的根结点
  208. * @param key 指定的key
  209. * @return Value 对应的value
  210. */
  211. private Value get(Node x, Key key) {
  212. if (x == null) {
  213. return null;
  214. }
  215. //比较当前结点x的键和参数key的大小
  216. int comp = key.compareTo(x.key);
  217. if (comp < 0) {
  218. //key比当前结点x的键要小,则继续向x的左子结点所在的树进行查找操作
  219. return get(x.left, key);
  220. } else if (comp > 0) {
  221. //key比当前结点x的键要大,则继续向x的右子结点所在的树进行查找操作
  222. return get(x.right, key);
  223. } else {
  224. //key与当前结点x的键相等,则返回结点x的value值
  225. return x.value;
  226. }
  227. }
  228. //测试
  229. public static void main(String[] args) {
  230. //创建红黑树
  231. RedBlackTree<String, String> redBlackTree = new RedBlackTree<>();
  232. //插入元素
  233. redBlackTree.put("1", "张三");
  234. redBlackTree.put("2", "李四");
  235. redBlackTree.put("3", "王五");
  236. //获取树中元素个数
  237. int size = redBlackTree.size();
  238. System.out.println(size);
  239. //从树中获取元素
  240. String s = redBlackTree.get("1");
  241. System.out.println(s);
  242. }
  243. }

测试结果:

  以上是个人随手敲的demo,如有不正确的地方,可以在下方留言指正,谢谢。与各位CSDN的伙伴们共勉,每天记录一点点,每天进步一点点

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

闽ICP备14008679号