AVL Tree
今年更新的量达成了
维持树平衡的核心是rotate
函数,这个函数与splay
中的并无二致。
AVL
由于不像splay
一样容易变化,因此其insert
操作和remove
操作需要展开细说。
insert
操作前半部分和普通的BST
并无二致,即从根节点开始沿着BST
开始检查进入左子树还是右子树。最终找到一个空位置即可将这个key
值插入进去。而AVL
需要保证|height(left_subtree)-height(right_subtree)|<2
,因此,在我们完成一次插入操作之后,从插入位置到根节点的所有节点对应的子树的height
可能会发生改变,进而引起失衡,此时需要依赖于rotate
操作进行调整。假设在u
节点处失衡,那么调整前其两个儿子的高度一定是h
与h+2
,调整后一定是h+1
与h+1
,而在插入前其高度必为h+1
与h
。因此,通过调整后其高度不变,那么继续向上的所有父亲节点的高度也不会发生改变,故在insert
操作中至多只需要调整一次。调整的细节最后进行陈述。
delete
与BST
的删除操作是相同的。首先通过查找找到删除的目标节点,记为x
。若x
是叶子节点,直接断掉其和父亲的边;若x
只有一个子树,那么删去x
,让其儿子代替其位置即可;若x
同时存在两个子树,那么x
的后继节点必然存在于其右子树中,沿着右子树一直向左跳,即可得到x
的后继,设为v
节点。交换两个节点的key
值,然后将v
节点删掉,由于v
节点没有左儿子,故可以归约为前面两种情况中的一种。删除完之后需要向上调整,但删除不具有和插入相同的性质,即删除之后即使经过调整,其高度也可能会改变,进而引起更高祖先子树的失衡,因此需要向上到根节点进行调整。
现在陈述调整操作的情况。设x
节点失衡,y
是其高度较高的儿子,z
是y
高度较高的儿子。若y
是x
的左(右)儿子,且z
是y
的左(右)儿子,那么只需要将y
向上旋转一次即可。否则两个点是异侧儿子,连续旋转两次z
节点即可平衡。这个过程由画图可以得到,在这里略过。
最后是查询previous
操作,同BST
查找过程,如果能找到,直接返回。否则找到的是其可以插入的位置,设如果要插入这个key
值,我们找到的父亲是x
,我们先假装它被插入进来了,节点叫做v
。如果x
的key
小于给定的key
,那么显然这就是前驱。否则这是后继,那么我们只需要找到这个后继的前驱即可。从插入的位置v
开始向上,如果v
是其父亲的右儿子,那么必然有父亲节点小于key
,那么此时就是前驱(因此其他点和他必定存在大小关系,不可能相等)。否则令v
等于其父亲,继续向上查找。
由AVL
的性质,可以知道其树高无论何时都是\(O(\log(n))\)级别的。
insert
操作中,我们找到其插入位置不会超高树高次,即\(O(\log(n))\)次。而向上调整沿着父亲走,次数仍然不超过树高\(O(\log(n))\)。因此,插入操作的总复杂度单次为\(O(\log(n))\)。
delete
操作中,找到目标节点的复杂度为\(O(\log(n))\),删除后的调整次数也是\(O(\log(n))\)。故总复杂度为\(O(\log(n))\)。
查询操作中,仍然还是找到目标位置,故复杂度为\(O(\log(n))\)。
综上,AVL
单次复杂度为\(O(\log(n))\),总复杂度为\(O(n\log(n))\)。