红黑树Java语言实现

红黑树的用途

用于快速查找元素,或者说快速根据关键字查询值。和散列表不同,散列表是利用散列函数建立单向映射实现快速查找。红黑树属于二叉查找树,它使得树的深度保持在O[ log(n) ],查找次数不多于最大深度,从而实现快速查找。

红黑树的定义

  1. 树中有两种颜色的节点,红色和黑色(顾名思义)
  2. 所有终端节点为黑色。终端节点(又称叶子节点)就是null节点
  3. 根节点是黑色
  4. 不能有两个连续的红色节点(即每个红色节点都有2个黑色节点)
  5. 从根节点到终端节点路径上的黑色节点个数相同

红黑树高效的原因

上述4、5最重要,找到终端可谓是最糟糕的情况了,即便的最坏的情况下,黑色节点都相同。那么路径最长的情况下是红黑相间的情况下,最短情况是全部是黑色。显然,到终端最长路径不超过最短路径的2倍。因此能够保证树的深度是O[ log(n) ]的(本人未证明),从而保证了效率。

红黑树的插入

首先要声明一下,除了插入根节点之外,新插入的节点初始的颜色都是红色的(后来可能会调整颜色)

1. 最简单的情况——插入根节点

只需保证根节点为黑色,非常简单。。。
在这里插入图片描述

2. 也很简单的情况——新节点的父亲是黑色的

在这里插入图片描述

没有任何负面影响,因为即保证了红色节点不连续,又不影响根节点到终端的黑色节点个数。原来是(黑)父亲+(黑)孩子,现在是(黑)父亲+(红)新节点+(黑色)新节点的儿子

3. 新节点的父亲是红色的

这时候就出现了红色节点连续的情况了,需要调整

3.1 新节点的叔叔是红色的

在这里插入图片描述
把爷爷变成黑色,父亲和叔叔变成红色。首先在从太爷爷到终端的黑色节点个数不变,其次这个局部没有红黑相间的情况。但是爷爷和太爷爷之间可能会出现2个连续红色的情况,把爷爷设为新节点(current),继续运行插入规律(上调)。

3.2 新节点的叔叔是黑色的

3.2.1 LL形式

在这里插入图片描述

扫描二维码关注公众号,回复: 11753146 查看本文章

这时候新的节点和父亲出现了红色相邻的情况。这时候对爷爷进行右单旋,并且爷爷和父亲交换颜色。注意的是可能会丢失父亲的右孩子,它本来就是介于父亲和爷爷的值,正好弥补了爷爷的左孩子。这样依旧保证了红黑树性质,因为父亲是黑色的,不需要上调。

3.2.2 LR形式

在这里插入图片描述

  1. 先说一种一步到位的方法,就是让父亲和爷爷成为新节点的左儿子和右儿子,同时新节点的左子树接到父亲的右子树,新节点的右子树接到爷爷的左子树。新节点和爷爷颜色交换,新节点设为黑色。
  2. 显然结果不存在连续的红色节点,根节点到终端的黑色节点数也保持不变。
  3. 新节点的左子树本身大于父亲小于新节点,新节点的右子树小于爷爷大于新节点,旋转后依然保持二叉搜索树的性质

在这里插入图片描述

  1. 再说一种两步到位的方法,先对父亲左单旋,注意新节点的左子树接到父亲的右子树上,否则会丢失节点!这样就转换成了3.2.1 LL形式(对爷爷右单旋)。从编程的角度来说还是一步到位更有效率!
3.2.3 RR形式

是LL的镜像对称模式,一切取反即可,不再累述

3.2.4 RL形式

是LR的镜像对称模式,一切取反即可,不再累述

红黑树 vs AVL树

  1. 总结来说,AVL树插入效率小于红黑树,AVL查询效率大于红黑树
  2. AVL可以保证左右子树高度差绝对值不大于1并且是一颗二叉搜索树
  3. 插入时,AVL树平衡性强,更容易引起不平衡触发耗时的旋转操作,红黑树只有在父亲是红色时才能触发旋转操作,“容纳\忍耐”能力强一些,因此损耗更低。
  4. 查询时候,AVL可以保证左右子树高度差绝对值不大于1,红黑树平衡性更差,所以深度略大,查询效率略低一些。
  5. 平均来说,红黑树性能>AVL性能
  6. AVL树介绍,参考我以前的博客:AVL树理解、证明

红黑树Java实现

参考我的实现:https://github.com/ghostorsoul/red_black_tree
代码解析未完待续。。。

猜你喜欢

转载自blog.csdn.net/weixin_42111859/article/details/108645379