AVL树与伸展树入门

基础

二叉查找树

        对树中的第一个节点X,它的左子树中所有项的值小于X中的项,而右子树中的项都大于X中的项。
        二叉查找树的平均深度是O(log N),因此二叉查找树的操作可以采用递归形式——因为递归不了几次。

增删改查

        查找类似于二分法。比根节点大,则找右子树,否则找左子树。
        修改也类似于查找,只不过在找到某个元素后对其进行修改。
        插入:找到合适的位置,然后生成一个新节点,并将该节点挂载到原二叉查找树中。
        删除:找到元素对应的节点,然后将其删除。删除该节点后,必须保证剩余的节点仍旧是一棵完整的二叉查找树。如果要删除的节点时叶子节点,则直接删除;如果要删除的节点M有一个孩子,则将其子树挂载到M的父节点上;如果要删除的节点有两个孩子,则选取左子树的最大值或右子树的最小值顶替M中的值,然后递归删除左子树中最大值或右子树最小值——找值代替M节点中的值,是为了避免将M移除后需要修改M父节点中指向孩子的链接。`

平衡树

        按照二叉树的插入,会导致二叉树退化成链表——例如依次插入1,2,3,4,……,N,此时形成的二叉树就只有右子树,已经退化成一个链表了——对于链表再进行增删改查,平均操作就不是O(log N)了。
        按照二叉树的删除方法也会导致二叉树两棵子树不平衡——因为一直删除左子树的最大值或右子树的最小值,必然导致左子树或右子树节点数目减少。
        解决上述问题的一种思路就是:附加一个平衡条件——任何节点的深度均不得过深

AVL树与旋转

        AVL树是一种最古老的平衡树,它的平衡条件是:每个节点的左子树和右子树的高度最多相差1。对于插入、删除等改变树高度的操作,会通过旋转使树满足平衡条件。
        假设删除采用的是懒惰删除(即不移除节点,只将节点标识为已删除),那么对于插入则会出现下面四种情况。假设M表示从叶子上溯到根的过程中出现的第一个不平衡点。
        1,对M的左儿子的左子树进行一次插入,简记左-左。
        2,对M的右儿子的右子树进行一次插入,简记右-右。
        3,对M的左儿子的右子树进行一次插入,简记左-右。
        4,对M的右儿子的左子树进行一次插入,简记右-左。
        上述四种情况,前两种可以使用单旋转进行解决,后两种要使用双旋转(即进行两次单旋转)。
        对于左-左:将M与M左儿子L进行旋转——将M变为L的右儿子,而L的右儿子变为M的左儿子。对于右-右也类似。
        对于左-右,将M与M的左儿子L与L的右儿子G进行旋转——先将G与L进行右-右旋转,再将G与M进行左-左旋转。
        具体
旋转可参考
维基百科

伸展树与展开

        伸展树:它保证从空树开始连续M次操作最多花费O(Mlog N)时间,虽然它不能保证单次操作花费时间为O(logN),也不排除单次操作花费为O(N),但能保证不存在坏的输入序列。
        伸展树基于这样的事实:每一次操作花费O(N)并不坏,只要它不经常发生就行。因为有的操作花费的时间多些,而有的操作花费的时候少些。
        伸展树的主要思路是:只要一个节点被访问到,它就必须被移动,就是必须通过一系列AVL树的旋转被推到根节点上。因为,一旦发现一个深层次的节点,我们就有可能不断对它进行访问。如果这个节点不改变位置,而每次访问又花费O(N),那么M次访问花费的时间就是O(M*N)。
        展开与旋转非常类似,也和与旋转完全相同的四种情况。记前一次操作所访问到的节点为M,其父节点为P,其祖父节点为G。
        如果MPG满足右-右模式(即M是P的右儿子,P是G的右儿子)或者左-左模式,那么展开与旋转完全一样。
        如果MPG满足左-左或右-右模式,那展开与旋转完全不同。展开是将M的祖先节点依照关系远近变成M的后代节点。如将P变成M的儿子节点,G变成P的儿子节点(也就是M的孙子节点)。具体例如参考(来源于数据结构与算法分析——java语言描述,第99页):
《AVL树与伸展树入门》

图4-54以图4-53在7处展开的结果。

    原文作者:AVL树
    原文地址: https://blog.csdn.net/java_c_android/article/details/54598751
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞