红黑树
思维导图
出现的背景,初衷,要解决的问题
背景:Rudolf Bayer在1972年发明了红黑树,称为“对称二叉B树”,但红黑树这个名字来源于Leo J Guibas 和 Robert Sedgewick在1978年写的一篇论文。
Rudolf Bayer是一名慕尼黑大学的信息学教授,(信息学是一门研究信息的收集、分类、操作、存储、检索和传播的科学),他发明红黑树的初衷是为了存储和有效地检索多维数据
简介:红黑树是一棵自平衡的二叉搜索树
应用场景:
1.C++中的map,set
2.Java中的HashMap,TreeMap,TreeSet
3.用于实现Linux中的CPU调度
优势和劣势
优势:
大多数地二叉搜索树检索信息的时间复杂度为O(h),h是二叉树的高,而且如果二叉树是一颗斜树,检索的时间复杂度会达到最差情况O(n),n是树的节点个数。
而红黑树检索的时间复杂度最多是O(logn)
劣势:
在插入,删除数据后需要进行调整平衡操作(染色,左旋,右旋)。
不适用于数据较少的场景
适用的场景
适用于海量数据,且需要经常进行插入删除操作的场景
类似的树和它的对比
AVL(平衡树)比起红黑树来说更加平衡,通俗的说就是查找性能可能更优;但平衡树在插入和删除时自旋的次数可能会大于红黑树,所以如果项目中插入删除节点操作更多选择红黑树会更好,如果查询操作更多则选择AVL树更好
底层原理,关键实现
源码
组成和关键点
红黑树节点需要遵守的规则
(满足以上规则的树大致是一个平衡树)
- 1.每个节点是红的或者黑的
- 2.根节点始终是黑的
- 3.红节点均不相邻(一个节点的父节点和子节点不会为红)
- 4.从任何节点(包括根节点)到达它子孙的NULL节点,经过的黑节点数相同
红黑树的一些特点
- 1.三个节点的链在红黑树中是不存在的
- 2.从一个节点到它最远叶子节点的节点数,不超过两倍到它最近叶子节点的节点数
- 3.红黑树有n个节点,它的高h<=2log2(n+1)
黑高
- 黑高是指从根节点到叶子节点经过的黑节点数(包含黑叶子节点)
- 性质:黑高>=树高/2
保持平衡
-
变色
- 颜色由红变黑或由黑变红
-
左旋
- 以某个节点作为支点,它的右子节点变为它的父节点,右子节点的左节点变为它的左节点,右子节点的右节点不变
-
右旋
- 与左旋相反,类比即可
插入
-
操作
- 查找插入位置
- 插入后的自平衡
-
情景分析
-
1.红黑树为空
- 把插入节点作为根节点,并设置为黑
-
2.插入节点key已存在
- 用新节点value值覆盖旧的value
-
3.插入节点的父节点为黑节点
- 由于插入的节点是红色的,如果插入节点的父节点是黑色时,不会影响红黑树的平衡,直接插入即可,无需做自平衡调整
-
4.插入节点的父节点为红色
-
4.1叔叔节点存在并且为红
- 由于不能存在两个相邻的红节点,所以祖父节点为黑;因此插入节点三代的颜色为:黑红红,显然最简单的处理方法就是将之变为:红黑红
- 处理方法:
1.先将父节点和叔叔节点变黑
2.将祖父节点变红
3.将祖父节点设置为当前节点,进行后续处理 - 1.祖父节点在变色后是红色,如果祖父节点的父节点是黑色,那就不需要做任何处理
2.如果祖父节点的父节点是红色,就违背了红黑树的性质:红色节点不能相邻;需要将祖父节点设置为当前节点,继续做自平衡操作
-
4.2叔叔节点不存在或者为黑,并且插入节点的父节点是祖父节点的左子节点
-
4.2.1新插入节点,为其父节点的左子节点(LL红色情况)
- 处理:将祖父节点设为红,父节点设为黑,进行右旋
-
4.2.2新插入节点,为其父节点的右子节点(LR红色情况)
- 1.将父节点作为当前节点,进行左旋
2.此时与4.2.1的情况相同(LL双红)将左旋后的父节点继续作为当前节点,按4.2.1处理
- 1.将父节点作为当前节点,进行左旋
-
-
4.3叔叔节点不存在或者为黑,插入节点的父节点是祖父节点的右子节点,与4.2对应
- 4.3.1RR
- 4.3.2RL
-
-
红黑树手写(含打印函数):
//红黑树类
/**
* 1.创建RBTree,定义颜色
* 2.创建静态内部类 RBNode
* 3.辅助方法定义: parentOf(node), isRed(node), isBlack(node), setRed(node),setBlack(Node),InOrderPrint()
* 4.左旋方法定义: leftRotate(node)
* 5.右旋方法定义: rightRotate(node)
* 6.公开插入接口方法定义: insert(K key, V value)
* 7.内部插入接口方法定义: insert(RBNode node)
* 8.修正插入导致红黑树失衡的方法定义: insertFixUp(RBNode node)
* 9.测试红黑树的正确性
*
* @param <K>
* @param <V>
*/
public class RBTree<K extends Comparable<K>, V> {
private static final boolean RED = true;
private static final boolean BLACK = false;
private RBNode root;
// 打印红黑树
public void padding ( String ch, int n )
{
int i;
for ( i = 0; i < n; i++ )
System.out.printf(ch);
}
void print_node (RBNode root, int level )
{
if ( root == null )
{
padding ( "\t", level );
System.out.println( "NIL" );
}
else
{
print_node ( root.right, level + 1 );
padding ( "\t", level );
if(root.color == BLACK)
{
System.out.printf("(%d)\n", root.key );
}
else
System.out.printf( "%d\n",root.key );
print_node ( root.left, level + 1 );
}
}
void print_tree()
{
print_node(this