红黑树和AVL树一样也是一颗平衡的二叉树
先说一下规定:
1.每个节点都为红色或者黑色
2.根节点必须为黑色
3.叶子节点必须是黑色的(就是null节点)
4.红节点的父节点和孩子节点都不能为红节点
5.每个节点起始到叶子节点任何路径的黑节点是相同的
它虽然是一颗平衡树但是并不是绝对的平衡树,比如avl树规定每一节点的子树的深不超过差值1,不过这种平衡树花的代价太大,所以采用的情况很少。
红黑树它的两个子树的差值最深子树是最浅子树的二倍,已经大概率提高了查找效率。最深便是红黑交替,最浅只有黑色节点
我们如何定义红黑树的一个节点;
我们看看节点应该有的属性,其左右孩子,数据,父节点,还有记录颜色的值。一共只有五个数据
java代码:
class RBTreeNode{
RBTreeNode parent;//父节点
RBTreeNode leftChild;//左孩子
RBTreeNode rightChild;//右孩子
int data;//数据
boolean color;//颜色只有红黑两种,所以直接用boolean值来确定,黑为true'红为false
public RBTreeNode(int data) {
this.data=data;
}
public RBTreeNode(RBTreeNode parent,RBTreeNode leftChild,RBTreeNode rightChild,int data,boolean color) {
this.parent=parent;
this.leftChild=leftChild;
this.rightChild=rightChild;
this.data=data;
this.color=color;
}
}
一、查找
红黑树的查找和二叉树的查找相同
二、遍历
遍历和二叉树的遍历没有区别
三、插入
在插入的时候我们认为每次插入的都是红色节点
1.我们插入一个节点时其父节点是黑色。
如图我们不用管其他节点如果其父节点为黑色,那么我们直接插入即可,因为要求每条路径黑色节点数相同,因为添加一个红色节点并不影响黑色节点的个数。不管插入的是左右孩子都没有影响。
2.插入节点的父节点是红色的
a.如果这时祖父的叔叔也是红色的
如图这个时候我们需要进行变色,将其祖父变为红色,其叔叔和父亲变为黑色
变化后
我们会发现这样变话后,其每条路径的黑色节点个数没有发生变化。所以也就变的平衡了
上图时祖父节点不是根节点,如果是根节点我们应该如何呢,这时我们变色后发现根节点是红色,这就违反了规则2,这时候其实很简单,我们直接将其父节点变为黑色即可
b.如果这时祖父的父亲是黑色,那么我们进行左旋,我们直接上图看步骤
这时候们进行左旋,我们先将60移到80的位置,然后把80移动为60的右子节点,且Z变为80的左子节点
左旋后
这个时候我们进行变色,这里变色我认为有两种变色方案
i.把50变为黑色
ii.把60变为黑色,50变为红色
不过我认为b是最好的,因为a还需要考虑60的父节点是不是红色
3.我们上边都考虑是在两个红色节点都是一个是一个的左子节点如果出现了右子节点的
这个时候我们进行右旋,直接上图
我们看到这种情况的时候然后进行右旋
首先将70移动到60的位置,然后把60移动为70的左子树,70原来的左子树即变为60的右子树
移动后,是不是发现此时又变成熟悉的样子了,这样我们继续
红黑树的一条数据插入就分这么几种情况,如果要是用代码实现的话。
大概分为12种情况(我认为,我将第一种情况分为两种,变色的情况分为4种,左旋4种,右旋两种),比如当直插入一条数据且其父亲为黑色时,它便分为两种一种时插如右节点,一种便是插入在左节点。
但是它变平衡的方法就是上边4种情况,只是在细节中不同。
我学习红黑树插入参考的资料:
https://blog.csdn.net/wu_ye123/article/details/78416099
https://www.sohu.com/a/201923614_466939
四、删除
现在我们来看删除,删除我把大的分类分为两种,一种删除的是叶子节点,一种删除的不是叶子节点
1.删除叶子节点
a.删除的节点是红色的。
例如:不管60在80的左子节点和右子节点,直接删除即可,无任何副作用。因为红色节点并不会影响规则5
b.删除的节点是黑色节点,这时候情况就比较多了,那我们一种一种来讨论
i.它的兄弟节点为红色节点。
如图删除60,此时我们应该我们发现删除60后80的左子树就少了一个黑色节点,我们这时就可以给它增加一个左节点,我们应用到了双旋转打法,首先将85移到80的位置(它兄弟的孩子节点代替了删除节点的父节点,这个时候删除节点父节点就成为了85的左孩子节点),然后80作为85的左节点,这就相当于增加了一个节点。当删除节点的兄弟节点为红色,那么它们颜色必定是图中样子颜色
ii.它兄弟节点为黑色但是它父节点为红色
这时我们发现好像找不到多余的节点让我们增加节点,这个时候那我们就开始变色
首先我们将其删除后,我们将其父节点变为黑色,那么我们发现父节点的左子树黑色节点数目没有变化,但是其右子树多了一个黑色节点,那么我们当然把右孩子节点变为红色了,这不就少了一个黑色节点嘛
下边时变化过程
iii.其父节点和兄弟节点都为黑色
这时我们发现通过变色和旋转都没有用之后,那么我们直接让这个树少一个黑色节点。直接将删除节点的兄弟节点变为红色
2.当进行删除后整个子树都少一个黑色节点后(对aiii的补充)
a.当删除子树的兄弟节点(500),为红色的时候
在这种情况下我们300的左子树少一个节点那么,我们就从其右子树拿过来一个节点,首先进行单旋转,即把500位置代替300为,300变为500的左子树,500的左子树400,变为300的右子树
这便是变换后的图片
变换后我们发现300的左右子树的节点数目不同那么这时我们就把400变为红色,这个时候500的左子数和右子树都少一个黑色节点,所以把500变为黑色节点。这样就成功了
这里的所有子树的父亲没有变化过所以压根就不用担心子树问题
b.子树的兄弟为黑色,但是其兄弟节点的左孩子是红色节点
没有颜色并不是它本来没有颜色而是它颜色不影响变换过程
400的左子树少一个黑色节点,我们就给左子数赠加一个黑色节点,遇到这种情况我们使用双旋转
首先我们把400移动到300的位置,然后让300成为400的左子树,400的左右子树分别成为300的右子树和500的左子树
双旋转后:
这个时候我们在进行变色,将300变为黑色,将400变为之前300的颜色
变色后:400和之前300颜色相同
c.其兄弟节点的右孩子为红色节点
没有标颜色说明其颜色并不会应该影响这步操作
300的左子树少一个黑色节点,那么我们通过单旋转给300的左子数增加一个黑色节点
将500移到300节点,在将300变为500的左子树,400变为其右子树‘
单旋转后
这个时候我们进行变色,将300节点变为黑色,500节点变为300节点之前的颜色
颜色变化后
d.其兄弟节点和兄弟节点的孩子节点都为黑色,但是其父节点为红色
遇到这种情况很简单直接变色即可,将父节点变为黑色,其兄弟节点变为红色
变色后
e.其兄弟节点,父节点,侄子节点都为黑色,那么这个时候一不做二不休的时刻又到了,直接让其兄弟节点变为红色也变的少一个黑色节点
变色
这里就是所有叶子节点所有可能都完成了,虽然最后一种又少了黑色节点,但是我们可以继续往上走。直到走到树树根那我们就不用继续走了。
3.现在来说一下删除的不是叶子节点
这个时候就简单多了
比如删除节点300,那么我们可以找左子树的最大值或者右子树的最小值来代替该删除的节点,因为其最大值和最小值必定为叶子节点,这个时候我们就可以认为其删除了叶子节点。叶子节点的删除就是前面所说的种类。
如果进行红黑树实现分的种类肯定会变多。但是只是细节发生了变化,其主要的原理就是上述的几种。
总的来说就是如果一个为红色节点的其中一颗子树少了一个黑色节点,那么我们就在这个节点的返回内,想办法再另一个子树不少黑色节点的情况下给这个子树加一个黑色节点。
如果是一个黑色节点的一颗子树少了一个黑色节点,那么首先看能不能通过变色和旋转的方法,给缺少一个黑色节点的子树加一个黑色节点。如果不能那么我们就直接让这个节点一下的所有路径都少一个黑色节点,然后再向上寻求帮助。
学习红黑树的删除参考:
https://www.cnblogs.com/tongy0/p/5460623.html
这里就是红黑树的插入和删除,它的查找和遍历就不说了,和二叉树是相同的。