红黑树
搞了4天,终于AC了…
平衡树是一种非常快的数据结构,比其他平衡树(Splay,Treap,FHQ)快一些
时间复杂度严格在 O ( log n ) O(\log n) O(logn) 之内
支持查找、删除、插入、前驱、后缀等操作
红黑树的前生是2-3树,也称B树
自己学去(其实是我懒)
红黑树的性质有5个:
-
每个节点只有黑色或者红色
-
根节点是黑色
-
NULL节点(空节点)是黑色
-
每个红色节点的子节点必须是黑色
-
每个节点到他的子树的所有NULL节点的路径上的黑色节点一样多
-
从性质5,我们可以推出,每个红色节点必须有两个黑色节点。(空节点也算黑色)
红黑树的插入:
我们插入的节点是什么颜色,当然是红色了,这样最少更改满足性质 5。
插入分为种情况
-
此树为空树。
-
插入节点的父亲为黑色
-
插入节点的父亲为红色
-
插入节点的叔叔为红色
-
插入节点的叔叔为黑色
-
插入节点的父亲是他爷爷的左儿子
-
插入节点的父亲是他爷爷的右儿子
-
-
I为插入节点,F为父亲节点,G为祖先节点,U为叔叔节点
情况1: 此树为空树
如果这棵树是空树的话,我们直接插入该节点,然后把该节点的颜色改为黑色,然后将该节点设为根节点。
情况2:插入节点的父亲为黑色
直接插入该节点,因为父亲是黑色不会影响到二叉树的性质。
情况3-1:父亲节点是红色,且叔叔节点是红色
我们将父亲节点设为黑色,叔叔节点设为黑色,爷爷节点设为红色。
但是如果爷爷节点的父亲节点是红色的呢?我们就需要对爷爷节点进行操作。
我们可以看作在整棵树里面插入爷爷节点和他的子树,这时候,我们需要对爷爷节点进行插入调整。
情况3-2-1:父亲节点是红色,且叔叔节点是黑色,而且插入节点的父亲是他爷爷的左儿子
我们先把自己变成父亲节点的左儿子。
把自己从右儿子变成左儿子的方法(当然如果已经是左儿子就不用了):
-
把自己变成原来的自己的父亲。
-
左旋自己。
将父亲颜色设为黑色,将爷爷颜色设为红。
然后右旋。
情况3-2-2:父亲节点是红色,且叔叔节点是黑色,而且插入节点的父亲是他爷爷的右儿子
其实这里处理方法就是上面的翻转:
如果自己是父亲的左儿子:
-
把自己变成原来的自己的父亲。
-
右旋自己。
将父亲颜色设为黑色,将爷爷颜色设为红。
然后左旋。(图…就不符了…太难画了)
那么我们红黑树的插入就搞定了
红黑树的删除:
删除分为3种情况;
1. 删除节点既有左子树也有右子树。
2. 删除节点只有左子树或者右子树。
3. 删除节点没有左子树没有右子树。
当删除节点左右两颗子树都有的时候,我们直接让删除节点的后继的值赋值给删除节点,然后把后继节点删除(可以看成和后继节点交换后删除原来要删除的节点),这里的删除也要用删除判断。
当删除节点只有一颗子树的时候,还有2种情况:
1. 删除节点有一颗左子树,而且根据性质 5,删除节点一定是黑色,而且左儿子肯定是红色。
2. 删除节点有一颗右子树,而且根据性质 5,删除节点一定是黑色,而且左儿子肯定是红色。
这时候,我们只需要把删除节点删除(直接删除,不用考虑其他问题),然后把左或右子节点改成黑色后替换到删除节点的位置(把子树也提上来)。
当删除节点没有子树,也就是说他是叶子节点的时候,还有4种情况:
-
删除节点为红色
-
删除节点为黑色
-
删除节点的兄弟节点为红色
-
删除节点的兄弟节点为黑色
-
删除节点的兄弟节点的左右子节点中,至少有一个是红色。
-
删除节点是他父亲的左子节点。
-
删除节点是他父亲的右子节点。
-
-
删除节点的兄弟节点的左右子节点中,全是黑色。
-
-
情况1:删除节点是红色。
我们直接删除,无影响。
情况2:删除节点是黑色且删除节点的兄弟节点为红色。
这时候,我们将父亲节点设为红色,将兄弟节点设为黑色。
然后向父亲节点往删除节点的方向旋转父亲节点。
这时候,这棵树还不是红黑树,我们需要继续判断下面的情况(当判断下面的情况的时候,删除节点不变,但是兄弟节点不是原来的兄弟节点,是当前的)
情况3:删除节点是黑色,删除节点的兄弟节点为黑色,删除节点的兄弟节点的左右子节点中,至少有一个是红色且删除节点是他父亲的左子节点。
这时候我们要兄弟节点的红色子节点调整为兄弟节点的右节点:
如果兄弟节点的左节点是红色,把兄弟节点的左节点都变成黑色,然后把兄弟节点设为红色,再把兄弟节点右旋一下。
然后我们把红色子节点调整后,我们把兄弟节点的颜色设为父亲节点的颜色。
再把父亲节点和兄弟节点的右子节点的颜色都设为黑色。
左旋父亲节点。
情况4:删除节点是黑色,删除节点的兄弟节点为黑色,删除节点的兄弟节点的左右子节点中,至少有一个是红色且删除节点是他父亲的右子节点。
我不多讲了,这跟上面的差不多。
如果兄弟节点的右节点是红色,把兄弟节点的右节点都变成黑色,然后把兄弟节点设为红色,再把兄弟节点左旋一下。
然后我们把红色子节点调整后,我们把兄弟节点的颜色设为父亲节点的颜色。
再把父亲节点和兄弟节点的左节点的颜色都设为黑色。
右旋父亲节点。懒得附图
情况5:删除节点是黑色,删除节点的兄弟节点为黑色,删除节点的兄弟节点的左右子节点中,全是黑色。
太好了,现在兄弟节点没有红色借了。
我们只好把工作交给祖先了,我们把兄弟节点染成红色。
如果父亲是根节点,不用更改。
如果父亲是红色,把父亲设为黑色。
如果父亲是黑色,然后看成是删除父亲,对父亲进行删除调整(很容易理解,因为在父亲这里少了一个黑节点,所以我们可以看作删除父亲)
(还差一个删除 D 节点,懒得搞了)然后呢,我们的删除操作就结束了。
其他操作跟 T r e a p Treap Treap 一样,这里不讲了。