Java集合框架之四---------红黑树的性质及原理

1.原理介绍

了解红黑树之前需要了解二叉查找树(二叉排序数)

1.左子树上所有结点的值均小于或等于它的根结点的值。

2.右子树上所有结点的值均大于或等于它的根结点的值。

3.左、右子树也分别为二叉排序树。

为什么要用红黑树?

红黑树放弃了追求完全平衡,追求大致平衡,任何不平衡都会在3次旋转之内解决,平衡二叉树不行

红黑树是一种近似平衡的二叉查找树,它能够确保任何一个节点的左右子树高度差不超过二者中较低那个的两倍,具体来说红黑树是满足如下条件的二叉查找树

①每个节点要么是红色,要么是黑色

②根节点必须是黑色

③每个叶子节点都是黑色,指的是空节点

④红色节点不能连续(即,红色节点的孩子和父亲都不能是红色)

⑤对于每个节点,从该点到null(叶子节点之后)的任何路径,都含有相同个数的黑色节点

性质⑤确保没有一条路径会比其他路劲长出两倍,因为如果红色不能连续出现两次,所以红色之后一定是黑色,如果长过两倍,那么一定会多一个黑色

在树的结构发生变化时(插入删除操作),往往会破坏上述条件3或4,需要通过调整使得查找树重新满足红黑树的条件

2.红黑树的基本操作

2.1左旋(相对于右孩子的右孩子后面插入节点会不平衡)

步骤:

①得到x的右子树,也就是y

②将x的右孩子指向y的左孩子

③y的左孩子的父亲指向x

④y的父亲指向x的父亲(因为对父亲赋值没有方向,可以先设置)

---如果x的父亲为空,表明根节点,根节点直接指向y

---如果x是父亲的左孩子,则x的父亲的左孩子指向y

---如果x是右孩子,则x的父亲的右孩子指向y

⑤y的左孩子指向x,x的父亲指向y

2.2右旋(相对于左孩子的左孩子后面插入节点会不平衡)

①获取y的左孩子,赋值为x

②将y的左孩子指向x的右孩子

③x的右孩子的父亲指向y

④将x的父亲指向y的父亲

⑤如果y的父亲为空,就将根节点指向x

-----如果y是左孩子,就将y的父亲的左孩子指向x

-----如果y是右孩子,就将y的父亲的右孩子指向x

2.3红黑树的插入操作

①将红黑树当成是二叉查找树,将节点插入

②将插入的节点当成--红色

将插入的节点着色为红色,不会违背"特性(5)"!少违背一条特性,就意味着我们需要处理的情况越少。

③通过一系列的旋转着色,使之重新成为红黑树

先理解插入的过程,后理解调整的过程

插入过程

①新建节点“y”,将y设为空节点

②将红黑树T的根节点指向x

③找到要插入的节点z的位置y

----循环x!=null

-------x复制给y,此时要的是满足条件的那个节点作为父节点,如果取x,那么循环退出的时候,x=null,因此要找到x之前访问的节点,即用y表示

-------如果z的值小于x的值

----------x=x的左孩子

-------否则 x = x 的右孩子

④设置z的父亲为y

⑤判断y是否是空节点

----如果是,将z设为根节点

----如果不是且z的值小于y的值,将z作为y的左孩子

----否则z作为y的右孩子

⑥此时z已经与y进行连接,只需设置左右孩子为空即可完成插入

⑦颜色设置为红色

⑧关键步骤:调整为红黑树

调整过程:

当红色节点被插入后,可分为三种情况:

①该红色节点插入后为根节点,将此节点涂为黑色

②该红色节点插入后的父节点是黑色,不需要做调整

③该红色节点插入后的父节点是红色,

这里分析为什么父节点是红色,一定有祖父节点,因为父节点是红色,显然不会是根节点,因为根节点是黑色的,所以如果祖父不存在,那么不满足性质②

上述3中情况处理问题的核心思想就是:将红色的节点移到根节点,然后将根节点设为黑色

Case1:叔叔是红色

现象:当前节点(即,被插入节点)的父节点是红色,且当前节点的祖父节点的另一个子节点(叔叔节点)也是红色

处理:

--将“父节点”设置为黑色

--将“叔叔节点”设置为黑色

--将“祖父节点”设置为红色

--将祖父节点设置为当前节点(红色节点),即之后继续对当亲节点进行操作

原因:此时的情况时,插入节点红色,父亲和叔叔是红色,这显然不符合红色不连续的规则,然后将父亲变黑,但是父亲变黑后,包含父节点的分支的黑色节点的总数会加1,因此需要将祖父变红,但是祖父变红后又会引起,祖父和叔叔连续红色,并且包含叔叔的节点的黑色数会减1,因此需要同时变化。

 

Case2叔叔是黑色,且当前节点是右孩子

当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右孩子

策略:

①将父节点作为“新的当前节点”

②以“新的当前节点”为支点进行左旋

为什么这么处理?

先讨论左旋,10是5的右孩子,处理红黑树的思想是,将红色节点移到根节点,然后根节点变为黑色,要使得10上移,10又是右孩子,因此左旋上移

此时5变为10的左孩子,如果此时10为根节点,那么直接将其设置黑色,结束;如果10不是根节点,需要执行步骤①,将5设为新的当前节点,为什么不继续以10为当前节点,我们处理问题要自下而上,即先解决孩子问题,在解决父亲问题。也可这么理解,由于需要先执行步骤①,然后在左旋,但是旋转传入的需要旋转的节点10的父亲,那么为了得到父亲,父亲作为新的当前节点

Case3叔叔是黑色,当前节点是左孩子            

当前节点的父亲节点是红色,叔叔节点是黑色,且当前节点是父节点的左孩子

①将父节点设置为黑色

②将祖父几点设置为红色

③以祖父节点为支点右旋

2.4删除

①将红黑树当成一棵二叉查找树,将节点删除

---被删除节点没有儿子,即叶子节点,那么直接将节点删除

---被删除节点只有一个儿子,那么,直接删除该节点,并用该节点的唯一孩子代替其位置

---被删除节点有两个儿子,先找出它的后继节点,然后将后继内容复制给被删除的节点,然后将后继节点删除。后继节点:左子树最大,或者右子树最小,因为这么做,如果左子树最大k覆盖删除节点,则剩下左子树都比k小,满足平衡;如果右子树最小k覆盖删除节点,则剩下右子树都比k大,也满足平衡,至于删除后继节点,如果没有儿子按照第一种;如果只有一个儿子,那么按照第二种

②通过旋转着色调整红黑树,什么时候才会触发调整,只有在删除的是黑色的时候,因为黑色删除后破坏黑色节点的个数属性

理解删除过程在进行调整

删除过程:删除节点为p

①如果p有两个孩子

----找到其后继节点,然后对p进行代替操作

----首先将p指向的节点的值代替,然后让p指向后继节点,后面对后继节点进行操作

②要么p无孩子,要么只有左右其中一个孩子

---如果左孩子不为空,代替节点r指向p的左孩子

---如果左孩子为空,代替节点r指向p的右孩子

③如果r不为空,表明只有一个孩子

---r的parent指向p的parent

----如果p的parent为空,说明p是根节点,则root指向r;

----如果p是parent的左孩子,p的parent的左孩子执行r,否则指向右孩子

----删除p,左右父亲=null

----如果p的颜色为黑,调整

④如果r为空,且p的父亲为空,表明只有一个p节点,因此root =null

⑤如果r空,p的父亲不空,则此时p没有孩子,删除即可

----如果p的颜色为黑,调整

----如果p的父亲不为空

-------p是左孩子,则p的父亲的左孩子为空,否则p的父亲右孩子为空,

-------p的父亲为空。

调整过程

  1. x指向一个红+黑节点,此时将x设为一个黑节点就行
  2. x执行根,此时将x设为一个黑节点即可
  3. 非前面两种状态

①情况说明:x是红黑节点

  处理:直接将x设为黑色,结束

②x是黑黑节点,x是根

  处理:什么都不做,结束

③x是黑黑节点,x不是根

  处理:四种情况

Case1:x是黑黑节点,x的兄弟是红色

说明:x"+"节点,x的兄弟节点是红色。(此时x的父节点和x的兄弟节点的子节点都是黑节点)

处理:

①x的兄弟设为黑色

②x的父亲设为红色

③x的父亲左旋

④左旋后,重新设置x的兄弟节点

为什么这么处理:将case1转换为case2,3,4;从而进一步处理,对x的父节点进行左旋,左旋后,兄弟变为父亲,因此左旋前,需要将x的兄弟节点设为黑色,父节点设为红色,左旋后设置新的兄弟节点

Case2:x是黑黑节点,x兄弟是黑色,x的兄弟节点的两个孩子都是黑色

处理:

①x的兄弟节点设为红色

②x的父节点设为新的x节点

Case3 x是黑黑节点,x的兄弟节点是黑色,x的兄弟节点左孩子红色,右孩子黑色

处理:

①x的兄弟节点的左孩子设为黑,

②x兄弟节点设置为红色

③对x的兄弟节点进行右旋,

④右旋后,重新设置x的兄弟节点

Case4 x是黑黑节点,兄弟是黑的,兄弟的右孩子是红色,左孩子任意

处理;

①将父节点颜色给兄弟节点

②将x父节点设为黑色

③将x兄弟节点的右孩子设为黑色

④对父节点进行左旋

⑤设置x为根节点

猜你喜欢

转载自blog.csdn.net/huangwei18351/article/details/82111124
今日推荐