JAVA集合:TreeMap红黑树深度解析
规则:
每个节点都有颜色(红或黑);根节点必须是黑色的;叶子节点(null节点)是黑的,即每个节点都有两个子节点(其中一个或者两个可能是null节点);相连节点不能都是红色(红色节点的父节点和子节点必须为黑色);任意节点到它所有的叶子节点的路径都含有相同的黑色节点的数量。
结构:
.
.
【引申规则:根据规则4和5,如果一个节点只有一个子节点,那么这个子节点肯定是红色的并且没有子节点。(如上图的22节点和65节点)】
基本方法:
获取节点颜色,叶子节点(null节点)的颜色是黑色
.
变化:
为了保证规则5成立,插入节点的颜色总是红色的,但这时候可能会造成规则4不成立,就需要进行调整,红黑树有两种调整操作:
变色(改变节点的颜色)旋转(左旋转和右旋转)
左旋转示意图(对节点E左旋转)
.
.
右旋转示意图(对节点S右旋转)
.
.
put方法:
.
.
fixAfterInsertion插入新节点之后的调整函数(重点):
.
.
.
示例:
以下图红黑树为例
.
现在我们要增加一个节点50,放在节点47的右子树上。
.
新增节点和父节点冲突,叔父节点是红色的,进行变色操作,把父亲节点和叔父节点都变成黑色,祖父节点变成红色,然后再对祖父节点进行调整。
.
.
叔父节点y是黑色的,并且x是右孩子,先进行左旋转,把红色节点转移到左分支。
.
.
再把x的父节点变黑,祖父节点变红,然后把祖父节点右旋转。
.
.
最多两次旋转即可解决冲突。
delete方法:
.
.
.
successor方法(查找继承者):
当需要删除的节点有两个孩子节点时才调用此方法。即右孩子节点 != null
.
fixAfterDeletion删除节点的调整函数(重点)
算法思想:我们要删除一个黑色节点,这会破坏规则5,调整有3种情景:
如果兄弟节点是红色的,经过变色旋转,在x节点上面增加一个红色的父亲节点,并且不破坏其他分支的黑色节点数量。兄弟节点是黑色的,如果兄弟节点的子节点都是黑色的,直接把黑色节点变红,即减少兄弟分支的黑色节点数量,然后对其父节点进行调整;兄弟节点是黑色的,但其有红色的孩子节点,不能直接变红。如果左孩子是红色节点,经过变色和右旋转把红色节点移到右边。此时再经过变色,并对x的父节点进行左旋转,在x节点的上面增加一个黑色节点,并且不破坏其他分支的黑色节点数量,调整结束。
.
.
.
示例:
删除的节点只有一个子节点(删除390),根据上面的引申规则,这个节点肯定是黑色,子节点是红色
.
删除红色的节点并且没有子节点(删除833)
.
删除黑色的节点并且没有子节点(删除22)
兄弟节点是红色的情况
.
变色+旋转,给x节点增加一个红色的父亲节点
.
.
此时x的新兄弟节点是黑色,并且孩子节点全是黑色(叶子节点是黑色的),把兄弟节点变色,然后x指向父节点,while循环继续调整。
.
.
节点是红色,跳出循环。循环外把该节点变黑。
.
.
方法返回后,deleteEntry方法把22节点删除,整个过程结束。
.
.
兄弟节点是黑色的情况
上面是旋转变化过程中其实已经遇见了这种情况,并且兄弟节点的孩子节点全是黑色,可以直接变色处理,下面来看一下,兄弟节点是黑色,并且有孩子节点是红色的情况
继续上面的红黑树,下面删除65节点
.
兄弟节点是黑色,并且有红色的孩子节点,针对x是左孩子的情况下,如果红色节点是左孩子,需要通过旋转操作移到右边
.
.
然后再进行变色旋转操作,给x节点增加一个黑色的父节点。x = root结束循环。
.
.
方法返回,deleteEntry方法把65节点删除,整个过程结束。
删除节点有两个孩子节点的情况。
.
删除节点55,该节点有两个孩子节点,deleteEntry方法中会查找继承者节点,即图中的65节点,把65节点的key和value赋值给55节点,然后转化为删除65节点。
.
.
因为继承者节点没有左孩子节点,所以这个问题又变成了删除一个孩子节点或者无孩子节点的问题。(参照上面)
OK,至此红黑树的分析就结束了,希望大家给予意见!