本文整合于以下两篇博文:
【https://www.jianshu.com/p/e136ec79235c 作者:安卓大叔 】
【http://www.wjhsh.net/ganxiang-p-13993340.html】
侵权请联系删除!!!侵权请联系删除!!!侵权请联系删除!!!
了解红黑树的插入和删除
什么是红黑树
- 红黑树(Red Black Tree) 是一种自平衡二叉查找树,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但 对之进行平衡的代价较低, 其平均统计性能要强于AVL ;
- 红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
- 红黑树虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。
- 红黑树是一种数据结构,典型的用途是实现关联数组。例:HashMap;
提示:以下图片来源于网络,如侵权请联系删除!!!
一、红黑树的规则?
1. 红黑树的特征(重要⭐⭐⭐⭐⭐)
- 根结点是黑色;
- 每个节点要么是黑色,要么是红色;
- 所有叶子都是黑色。(叶子是NIL结点);
- 每个红色结点的两个子结点都是黑色,且不能有两个连续的红色结点;
- 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点;
a. 从5可以推导出【如果一个结点是黑结点,那么该结点肯定有两个子结点】
2. 左旋
目的
为了修复,平衡红黑树。因为添加或删除红黑树的节点之后,红黑树就发生了变化,可能就不满足红黑树的5条性质了,也就不是一颗红黑树了。而通过旋转可以使这棵树重新成为红黑树,即旋转的目的就是为了保证红黑树的特性。
定义:
以某个结点作为旋转点,其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
演示图:
静态图:
动态图:
3. 右旋
目的
为了修复,平衡红黑树。因为添加或删除红黑树的节点之后,红黑树就发生了变化,可能就不满足红黑树的5条性质了,也就不是一颗红黑树了。而通过旋转可以使这棵树重新成为红黑树,即旋转的目的就是为了保证红黑树的特性。
定义:
以某个结点作为旋转结点,其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
演示图:
静态图:
动态图:
二、查找流程图
因为红黑树是一颗二叉平衡树,并且查找不会破坏树的平衡,所以查找跟二叉平衡树的查找无异:
- 从根结点开始查找,把根结点设置为当前结点;
- 若当前结点为空,返回null;
- 若当前结点不为空,用当前结点的key跟查找key作比较;
- 若当前结点key等于查找key,那么该key就是查找目标,返回当前结点;
- 若当前结点key大于查找key,把当前结点的左子结点设置为当前结点,重复步骤2;
- 若当前结点key小于查找key,把当前结点的右子结点设置为当前结点,重复步骤2;
三、红黑树的插入
1. 插入流程图
插入操作包括两部分工作:一查找插入的位置,二插入后自平衡。
- 从根结点开始查找;
- 若根结点为空,那么插入结点作为根结点,结束。
- 若根结点不为空,那么把根结点作为当前结点;
- 若当前结点为null,返回当前结点的父结点,结束。
- 若当前结点key等于查找key,那么该key所在结点就是插入结点,更新结点的值,结束。
- 若当前结点key大于查找key,把当前结点的左子结点设置为当前结点,重复步骤4;
- 若当前结点key小于查找key,把当前结点的右子结点设置为当前结点,重复步骤4;
2. 插入场景
约定节点间关系叫法
插入场景
插入规则:新插入的节点都为红色
- 新节点位于根节点,其没有父节点时;
处理思路:将该节点直接设为黑色即可。
- 新节点的父节点已然是黑色时;
处理思路:不用动,这已然是一颗红黑树。
- 父节点和叔节点都是红色时;
处理思路:
a.将父节点和叔节点设为黑色;
b.将祖父节点设为红色;
c.将祖父节点设为当前节点,并继续对新当前节点进行操作;
- 父节点是红色,叔节点是黑色或不存在时,又分如下四种情况:
4.1)当前节点是父亲的左孩子,父亲是祖父的左孩子(Left-Left);
处理思路:
a.交换父节点和祖父节点的颜色;
b.将祖父节点右旋;
4.2)当前节点是父亲的右孩子,父亲是祖父的左孩子(Right-Left);
处理思路:
a.将父节点左旋,并将父节点作为当前节点;
b.然后再使用Left Left情形;
4.3)当前节点是父亲的右孩子,父亲是祖父的右孩子(Right-Right);
处理思路:
a.交换父节点和祖父节点的颜色;
b.将祖父节点左旋;
4.4)当前节点是父亲的左孩子,父亲是祖父的右孩子(Left-Right);
处理思路:
a.将父节点右旋,并将父节点作为当前节点;
b.然后再使用Right Right情形;
3. 插入演示
通过插入【12 1 9 2 0 11 7 19 4 15 18 5 】完成上述插入情形的展示。
插入规则:新插入的节点都为红色
-
插入数字12
【场景1】: 新节点位于根节点,其没有父节点时;
将该节点直接设为黑色即可。
-
插入数字1
【场景2】:新节点的父节点已然是黑色时;
不用动,这已然是一颗红黑树。
-
插入数字9
【场景4.2(Right-Left)】:当前节点是父亲的右孩子,父亲是祖父的左孩子;
a.将父节点左旋,并将父节点作为当前节点;
b.然后再使用Left Left情形;
【场景4.1(Left-Left)】当前节点是父亲的左孩子,父亲是祖父的左孩子;
a.交换父节点和祖父节点的颜色;
b.将祖父节点右旋;
-
插入数字2
【场景3】:父节点和叔节点都是红色时;
a.将父节点和叔节点设为黑色;
b.将祖父节点设为红色;
c.将祖父节点设为当前节点,并继续对新当前节点进行操作;
【场景1】:新节点位于根节点,其没有父节点时;
将该节点直接设为黑色即可。
。 -
插入数字0
【场景2】: 新节点的父节点已然是黑色时;
不用动,这已然是一颗红黑树。
-
插入数字11
【场景2】: 新节点的父节点已然是黑色时;
不用动,这已然是一颗红黑树。
-
插入数字7
【场景3】:父节点和叔节点都是红色时;
a.将父节点和叔节点设为黑色;
b.将祖父节点设为红色;
c.将祖父节点设为当前节点,并继续对新当前节点进行操作;
-
插入数字19
【场景2】: 新节点的父节点已然是黑色时;
不用动,这已然是一颗红黑树。
-
插入数字4
【场景4.4(Left-Right)】:当前节点是父亲的左孩子,父亲是祖父的右孩子;
a.将父节点右旋,并将父节点作为当前节点;
b.然后再使用Right Right情形;
【场景4.3(Right-Right)】:当前节点是父亲的右孩子,父亲是祖父的右孩子;
a.交换父节点和祖父节点的颜色;
b.将祖父节点左旋;
-
插入数字15
【场景3】:父节点和叔节点都是红色时;
a.将父节点和叔节点设为黑色;
b.将祖父节点设为红色;
c.将祖父节点设为当前节点,并继续对新当前节点进行操作;
-
插入数字18
【场景4.2(Right-Left)】:当前节点是父亲的右孩子,父亲是祖父的左孩子;
a.将父节点左旋,并将父节点作为当前节点;
b.然后再使用Left Left(4.1)情形;
【场景4.1(Left-Left)】:当前节点是父亲的左孩子,父亲是祖父的左孩子;
a.交换父节点和祖父节点的颜色;
b.将祖父节点右旋;
-
插入数字5
【场景3】:父节点和叔节点都是红色时;
a.将父节点和叔节点设为黑色;
b.将祖父节点设为红色;
c.将祖父节点设为当前节点,并继续对新当前节点进行操作;
三、红黑树的删除
1. 什么是前后继节点
在了解这个概念之前,先思考一个问题:
在删除节点的时候,删除的不一定是叶子节点,如果删除中间的某个节点,必然会导致树失衡,这就需要来调整树的平衡,但这会牵扯到很多点,调整起来会很麻烦。有没有什么方法能避免这种复杂的调整呢?只需要简单的调整即可满足需求呢??
解决方案
对于上面的问题,就有大佬提出在删除树的某个节点时,其实可以不必真的删除这个节点的位置,可用前继节点或后继节点中的随便一个来替代要删除的节点,将要删除的前继节点或后继节点直接填到待删除的节点,然后再把前继节点或者后继节点删除。这个时候问题就转化成了在二叉查找树中删除一个没有左子树的节点或者是一个没有右子树的节点,这时只需要对树做简单的平衡即可。
前继节点
定义: 前继结点是小于删除结点的最大结点,也是删除结点的左子树种最右结点。
例:以一个包含1~10的树为例,如果我们希望删除8所在的节点,此时可以用前继节点7来替代,7小于8,又是删除结点的最大结点,所以7存在删除节点8左边的最右侧。所以7就是8的前继节点。
后继节点
定义:后继结点是大于删除结点的最小结点,也是删除结点的右子树种最左结点。
例:以一个包含1~10的树为例,如果我们希望删除8所在的节点,此时可以用后继节点9来替代,9大于8,又是删除结点的最小结点,所以9存在删除节点8右边的最左侧。所以9就是8的后继节点。
练习
图上4的前继节点和后继节点分别是几?
答案:
前继节点是:3 (左子树种最右结点)
后继节点是:5 (右子树种最左结点)
2. 删除场景
删除场景1:
删除场景1与删除场景2是一样的,只不过一个是图形展示,一个是文字描述
看的比较清晰,以删除节点的替换节点(前继节点或后继结点)来描述的。
名词解释:
P: 父节点
S:兄弟节点 【SL:兄弟节点的左子节点】 【 SR:兄弟节点的右子节点】
删除场景2:
删除场景1与删除场景2是一样的,只不过一个是图形展示,一个是文字描述
删除场景是最为复杂的,此分为两步: 1. 对换数值 2. 旋转调色
以下删除演示皆来自【删除场景2】
以下删除演示皆来自【删除场景2】
以下删除演示皆来自【删除场景2】
1. 数值对换
从树中删除节点X(以寻找后继节点的方式进行删除)
- 情况1:如果X没有孩子:
1.1). 如果X是红色,直接删除X。
1.2). 如果X是黑色,则以X为当前节点进行旋转调色,最后删掉X。 - 情况2:如果X只有一个孩子C,交换X和C的数值,再对新X进行删除。根据红黑树特性,此时X不可能为红色,因为红色节点要么没有孩子,要么有两个黑孩子。此时以新X为当前节点进行情况①的判断。
- 情况3:如果X有两个孩子,从后继中找到最小节点D,交换X和D的数值,再对新X进行删除。此时以新X为当前节点进行情况①或②的判断
2. 旋转调色
N=旋转调色的当前节点[等于情况①中的X]
P = N的父亲
W = N的兄弟
Nf = N的远侄子
,
Nn = N的近侄子
- 情况A:N是根或者N是红色。
处理思路:直接将N设为黑色。
- 情况B:N不是根且N是黑色,且W为红色。
处理思路:
a.将W设为黑色,P设为红色。
b.对P进行旋转(N为P的左子时进行左旋,N为P的右子时进行右旋),将情况转化为情况1、2、3、4、5。
- 情况C:N不是根且N是黑色,且W为黑色,且W的左右子均为黑色。
处理思路:
a.将W设为红色。
b.将P设为当前节点进行旋转调色,将情况转化为情况1、2、3、4、5。
- 情况D:N不是根且N是黑色,且W为黑色,且Nf为黑色,Nn为红色。
处理思路:
a.交换W与Nn的颜色。
b.对W进行旋转(N为P的左子进行右旋,N为P的右子进行左旋),旋转后N的新兄弟W有一个红色WR,则转换为情况5。
- 情况E:N不是根且N是黑色,且W为黑色,且Nf为红色,Nn为黑色。
处理思路:
a.将W设为P的颜色,P和Nf设为黑色。
b.并对P进行旋转(N为P的左子进行左旋,N为P的右子进行右旋),N设为根。
- 情况F:N不是根且N是黑色,且W为黑色,且W的左右子均为红色。
处理思路:
a.将W设为P的颜色,P和Nf设为黑色。
b.并对P进行旋转(N为P的左子进行左旋,N为P的右子进行右旋),N设为根。
2. 删除演示
通过删除【12 1 9 2 0 11 7 19 4 15 18 5 】完成上述删除情形的展示。
-
删除数字12
删除节点12满足【数值对换情况3】:
a. 找到后继节点13,将删除节点12 与后继节点13进行数值对换;
b. 删除节点12为黑色,后继节点13为红色,满足**【数值对换情况1.2】**对删除节点12进行旋转调色;
删除节点12满足【旋转调色情况E】:
a. 将兄弟节点11设置为红色,兄弟节点的左子节点10和父节点13设置为黑色;
b. 删除节点12为父节点13的右子节点,以父节点13为旋转点,进行右旋,并删除节点12。 -
删除数字1
删除节点1满足【数值对换情况3】:
a. 找到后继节点2,将删除节点1 与后继节点2进行数值对换,得到【数值对换情况2】;
b. 根据【数值对换情况2】将删除节点1和子节点3的数值交换,得到【数值对换情况1.1】;
c. 根据【数值对换情况1.1】可直接删除节点1,无需进行旋转调色; -
删除数字9
删除节点9满足【数值对换情况3】:
a. 找到后继节点10,将删除节点9与后继节点10进行数值对换,满足**【数值对换情况1.2】**对删除节点9进行旋转调色;
删除节点9满足【旋转调色情况C】:
a. 将兄弟节点13设置为红色,父节点11设为当前节点进行旋转调色;
b. 当前删除节点为11,满足【旋转调色情况A】,则直接将当前节点11设置为黑色并删除节点9。 -
删除数字2
删除节点2满足【数值对换情况3】:
a. 找到后继节点3,将删除节点2与后继节点3进行数值对换,满足**【数值对换情况1.1】**对删除节点2进行旋转调色;
删除节点9满足【旋转调色情况C】:
a. 将兄弟节点0设置为红色,父节点3设为当前节点进行旋转调色;
b. .当前删除节点为3,满足【旋转调色情况A】,则直接将当前节点3设置为黑色并删除节点2。 -
删除数字0
删除节点0满足【数值对换情况1.1】:直接删除,无需旋转调色。 -
删除数字11
删除节点11满足【数值对换情况2】:
a. 将删除节点11和子节点13的数值交换,得到【数值对换情况1.1】;
b. 根据【数值对换情况1.1】,直接删除,无需旋转调色。 -
删除数字7
删除节点7满足【数值对换情况2】:
a. 将删除节点7和子节点8的数值交换,得到【数值对换情况1.1】;
b. 根据【数值对换情况1.1】,直接删除,无需旋转调色。 -
删除数字19
删除节点19满足【数值对换情况1.2】:
a. 以删除节点19进行旋转调色,得到【旋转调色情况F】;
满足【旋转调色情况F】:
a. 将兄弟节点16设为红色,兄弟节点的左子节点15和父节点18设为黑色;
b. 以父节点18为旋转点右旋并删除19。 -
删除数字4
删除节点4满足【数值对换情况3】:
a. 找到后继节点5,将删除节点4与后继节点5进行数值对换,满足**【数值对换情况1.1】**对删除节点4进行旋转调色;
满足【旋转调色情况C】:
a. 将兄弟节点8设为红色,将父节点6设为当前节点进行旋转调色;
b. 当前删除节点为6,满足【旋转调色情况A】,则直接将当前节点6设置为黑色并删除节点4。 -
删除数字15
删除节点15满足【数值对换情况1.2】:
a. 以删除节点15进行旋转调色,得到【旋转调色情况D】;
满足【旋转调色情况D】:
a. 将兄弟节点18设为红色,兄弟节点的左子节点17设为黑色;
b. 以兄弟节点18作为旋转点,右旋,得到【旋转调色情况E】;
c. 根据【旋转调色情况E】将兄弟节点17设置为红色,兄弟节点的左子节点18和父节点16设置为黑色, 删除节点15为父节点16的右子节点,以父节点16为旋转点,进行左旋,并删除节点15。 -
删除数字18
删除节点18满足【数值对换情况1.2】:
a. 以删除节点18进行旋转调色,得到【旋转调色情况C】;
满足【旋转调色情况C】:
a. 将兄弟节点16设为红色,将父节点17设为当前节点进行旋转调色;
b. 当前删除节点为17,满足【旋转调色情况A】,则直接将当前节点17设置为黑色并删除节点18。 -
删除数字5
删除节点5满足【数值对换情况3】:
a. 找到后继节点6,将删除节点5与后继节点6进行数值对换,满足**【数值对换情况2】;
b. 根据【数值对换情况2】** 将删除节点5和其子节点8进行数值对换。得到【数值对换情况1.1】;
c. 根据【数值对换情况1.1】,直接删除,无需旋转调色。
总结
本文整合于以下两篇博文:
【https://www.jianshu.com/p/e136ec79235c 作者:安卓大叔 】
【http://www.wjhsh.net/ganxiang-p-13993340.html】
侵权请联系删除!!!侵权请联系删除!!!侵权请联系删除!!!