AVL Tree

今年更新的量达成了

维持树平衡的核心是rotate函数,这个函数与splay中的并无二致。

AVL由于不像splay一样容易变化,因此其insert操作和remove操作需要展开细说。

insert操作前半部分和普通的BST并无二致,即从根节点开始沿着BST开始检查进入左子树还是右子树。最终找到一个空位置即可将这个key值插入进去。而AVL需要保证|height(left_subtree)-height(right_subtree)|<2,因此,在我们完成一次插入操作之后,从插入位置到根节点的所有节点对应的子树的height可能会发生改变,进而引起失衡,此时需要依赖于rotate操作进行调整。假设在u节点处失衡,那么调整前其两个儿子的高度一定是hh+2,调整后一定是h+1h+1,而在插入前其高度必为h+1h。因此,通过调整后其高度不变,那么继续向上的所有父亲节点的高度也不会发生改变,故在insert操作中至多只需要调整一次。调整的细节最后进行陈述。

deleteBST的删除操作是相同的。首先通过查找找到删除的目标节点,记为x。若x是叶子节点,直接断掉其和父亲的边;若x只有一个子树,那么删去x,让其儿子代替其位置即可;若x同时存在两个子树,那么x的后继节点必然存在于其右子树中,沿着右子树一直向左跳,即可得到x的后继,设为v节点。交换两个节点的key值,然后将v节点删掉,由于v节点没有左儿子,故可以归约为前面两种情况中的一种。删除完之后需要向上调整,但删除不具有和插入相同的性质,即删除之后即使经过调整,其高度也可能会改变,进而引起更高祖先子树的失衡,因此需要向上到根节点进行调整。

现在陈述调整操作的情况。设x节点失衡,y是其高度较高的儿子,zy高度较高的儿子。若yx的左(右)儿子,且zy的左(右)儿子,那么只需要将y向上旋转一次即可。否则两个点是异侧儿子,连续旋转两次z节点即可平衡。这个过程由画图可以得到,在这里略过。

最后是查询previous操作,同BST查找过程,如果能找到,直接返回。否则找到的是其可以插入的位置,设如果要插入这个key值,我们找到的父亲是x,我们先假装它被插入进来了,节点叫做v。如果xkey小于给定的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))\)

posted @ 2022-02-09 23:29  小蒟蒻yyb  阅读(995)  评论(9编辑  收藏  举报