红黑树是啥??
在我们了解红黑树之前,首先要知道二叉树, 二叉树是基于链表结构演化而来的可以弥补链表数据结构查询性能上的问题,
那么什么情况下二叉树的搜索性能高呢, 在二叉树平衡的情况下搜索性能是最高的。
那么红黑树数据结构就是用来维护二叉树的平衡性. (还有AVL树)
红黑树的定义:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点(NIL节点,空节点)是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
满足这五个性质的就可以称之为一个红黑树
下面我们用一个数组来转换成红黑树做例子:
首先定义一个数组:
int arr = {1,2,3,4,5,6,7,8,9,10};
1. 先插入元素 "1", 为了满足性质2, 那么"1"的颜色为黑色;
2. 我们插入元素 "2", 为了满足性质4,我们除根节点外,所有插入的元素都为红色, 因为这样可以满足性质4的所有叉上的黑元素相同
3. 我们继续插入元素"3"
这时我们看到这时违反了性质4 ,不能有相邻的红色节点.
我们看到元素3的父节点2是红色,且元素2的 兄弟节点为黑色(兄弟节点为空,也可视为黑色)
我们需要两步操作来处理
1). 先把元素2变为黑色, 元素1变为红色
2). 以节点 “1”位中心进行左旋转就变成了下图这样(左旋、右旋到后面会专门讲解一下)
4. 这时我们继续插入元素"4"
我们可以看到,这时又违反了性质4,不能有相临的两个红色节点
我们可以看到父节点和叔叔节点都是红色的, 遇到这种情况,我们之间把父节点和叔叔节点染成黑色,根节点染成红色即可
由于红黑树根节点必须是黑色的,所以我们把根节点改为黑色即可
最后就变成了上图这样
5. 我们继续插入元素"5" .
我们可以看到和上次一样 违反了性质4 ,不能有相临的两个红色节点
处理方法是 把父节点染为黑色,爷爷节点染为红色,然后以爷爷节点进行左旋转
1)
2)
6. 我们继续插入元素 “6”
我们看到又违反了性质4 , 和上次插入元素“4”一样,把父节点和叔叔节点颜色染为黑色,爷爷节点染为红色
7. 我们继续插入元素“7”。
这个还是违反了性质4 , 和上次插入元素“5”一样,把父节点染为黑色,爷爷节点染为红色然后把爷爷节点向左旋转
1)
2)
8. 我们继续插入元素8
这时,又违反了性质4, 老规矩,和插入元素“4”一样, 把父亲节点和叔叔节点染为黑色,爷爷节点染为红色
这时我们看到元素6和元素4相邻了,我们看到元素4的兄弟节点为黑色, 我们可以和原来操作元素“5”一样。
先把元素4染为黑色,再把元素2染为红色,然后以元素3位中心进行左旋转
1)
2)
9. 我们继续插入元素9
这种情况我们把 9的父节点染为黑色,爷爷节点染为红色, 然后把爷爷节点向左旋转即可. (处理好多次了,直接上图)
10 . 插入元素 "10"
对节点10的操作和原来操作节点 “4” 、“6”、“8”一样
对节点8的操作和原来操作 "3" 、“5”、“7”一样
最终效果如下:
我们再来试一个例子:
int arr = {10,9,8,7,6,5,4,3,2,1}
1. 插入元素10
2. 插入元素9
3. 插入元素8
遇到这种情况(父节点为红色,并且叔叔节点为黑色或者空)就是把父节点染为黑色,爷爷节点染为红色,然后爷爷节点进行右旋转(注意,这里可是右旋转),
1)
2)
4. 插入元素7
这种情况,父节点为红色,叔叔节点也为红色,那么把父节点和叔叔节点染为黑色,爷爷节点染为红色(如果爷爷节点为根节点,则必须为黑色)
效果如下:
5. 接着插入元素6
这种情况和插入元素8一样, 把父节点染为黑色,爷爷节点为红色,然后爷爷节点进行右旋转,最终效果如下
6. 插入元素5继续
老规矩,如果父节点和叔叔节点为红色,把父节点和叔叔节点染为黑色,爷爷节点为红色即可
7. 接着元素4
这种处理都处理了好多遍了, 把父节点染为黑色,爷爷节点染为红色,然后右旋转即可
元素3元素2元素1插入的逻辑就不一一述说了,相信大家看了上面的步骤都轻车熟路了,最终效果图如下:
我们上面实验了两个有顺序的数组, 那么我们最后再拿一个乱序的数组来试试。
int arr = {6,8,7,9,5,3,4,1,2}
我们来试试这个乱序的数组转换成红黑树.
1. 插入元素6
2. 插入元素8
3. 插入元素7
这时我们看到,我们以前没处理过这种情况的啊, (我们以前处理的是当前节点处于的位置和父节点处于的位置都是位于上一层节点的左边或者右边 )
遇到这种情况我们先把父节点右旋转一下.
就变成了这种,这种结构是不是似曾相识啊...
对, 变成这样后把父节点颜色改为黑色,爷爷节点为红色,然后左旋转即可
1)
2)
4 .好,我们下一步插入元素9
这种情况(喂喂,这种情况处理好多次了啊,不会就有点过不去了), 把父节点和叔叔节点改为黑色,爷爷如果不是根节点则改为红色。效果图
5. 我们插入元素5,
这个直接加入即可
6. 我们插入元素3
这种情况处理很简单,把父节点改为黑色,爷爷节点改为红色, 然后右旋转
7. 我们继续插入元素4
这种情况直接把父节点和叔叔节点改为黑色,爷爷节点改为红色即可
8. 继续, 我们插入元素1
直接插入即可
9. 最后,插入元素2 (具体步骤自己意淫去....有了上面的铺垫应该可以想出来的)
最终效果如下:
好了, 我们插入了三个元素作为例子, 相信大家对红黑树的插入应该有了解了,无非就是左旋右旋调整树的平衡性,
我在这里大概总结下规律
1. 如果插入的元素的父节点和叔叔节点都为红色。
处理方法:直接把叔叔节点和父节点染为黑色,爷爷节点要不是根节点的话染为红色即可
2. 如果父节点为红色,叔叔节点为空或者为黑色,并且父节点是爷爷节点是又子树,并且当前节点是父亲节点的左子树。
处理方法:先把父节点右旋转一下, 然后把父节点染为黑色,爷爷节点染为红色,然后左旋转一下.
3. 如果父节点为红色, 叔叔节点为空或者黑色,并且父节点是爷爷节点的右子树,并且当前节点是父节点的右子树
处理方法:把父节点染为黑色,爷爷节点染为红色,然后左旋转一下.
4. 如果父节点为红色,叔叔节点为空或者黑色,并且父节点是爷爷节点的左子树,并且当前节点是父节点的右子树
处理方法: 先把父节点左旋转一下, 然后把父节点染为黑色,爷爷节点染为红色,然后右旋转一下.
5. 如果父节点为红色,叔叔记得为空或者黑色,并且父节点是爷爷节点的左子树,并且当前节点是父节点的左子树
处理方法:把父节点染为黑色,爷爷节点染为红色,然后右旋转一下.
就这五种。