美文网首页iOS 开发每天分享优质文章
数据结构与算法之AVL树(九)

数据结构与算法之AVL树(九)

作者: 路飞_Luck | 来源:发表于2019-05-26 16:31 被阅读56次
    目录
    • 基本概念
    • 添加节点后恢复平衡操作
      • 处理失衡的方法 LL,RR,LR,RL
      • 图解 + 核心代码实现 + 原理讲解
    • 删除节点后恢复平衡操作
    • 处理失衡的方法 LL,RR,LR,RL
    • 总结
    一 简介

    AVL树是最早发明的自平衡二叉搜索树之一

    备注:AVL 取名于两位发明者的名字
    G. M. Adelson-VelskyE. M. Landis(来自苏联的科学家)

    1.1 几个概念
    • 平衡因子(Balance Factor):某结点的左右子树的高度差
    • AVL树的特点
      • 每个节点的平衡因子只可能是 1、0、-1(绝对值 ≤ 1,如果超过 1,称之为“失衡”)
      • 每个节点的左右子树高度差不超过 1
      • 搜索、添加、删除的时间复杂度是 O(logn)
    image.png
    1.2 平衡对比

    输入数据:35, 37, 34, 56, 25, 62, 57, 9, 74, 32, 94, 80, 75, 100, 16, 82

    • 普通二叉搜索树 AVL树


      二叉搜索树.png
    • AVL树

    AVL树.png
    1.3 添加导致的失衡

    示例:往下面这棵子树中添加 13

    • 最坏情况:可能会导致所有祖先节点都失衡
    • 父节点、非祖先节点,都不可能失衡
    添加节点失衡.png
    二 处理失衡
    2.1 LL - 右旋转(单旋)
    LL.png

    伪代码:

    1.g.left = p.right
    2.p.right = g
    3.让 p 成为这棵子树的根节点
    4.仍然是一棵二叉搜索树,T0 < n < T1 < p < T2 < g < T3
    5.还需要维护:
    5.1 T2,p,g的 parent 属性 
    5.2 先后更新 g,p的高度
    
    2.2 RR - 左旋转(单旋)
    RR.png
    • 伪代码实现
    1. g.right = p.left
    2. p.left = g
    3. 让p成为这棵子树的根节点
    4. 仍然是一棵二叉搜索树:T0 < g < T1 < p < T2 < n < T3 ◼ 整棵树都达到平衡
    5. 还需要注意维护的内容 
    5.1 T1、p、g 的 parent 属性 
    5.2 先后更新 g、p 的高度
    
    2.3 LR – RR左旋转,LL右旋转(双旋)
    LR.png
    2.4 RL – LL右旋转,RR左旋转(双旋)
    RL.png
    三 代码实现

    新建一个类AVLTree继承自BST,并重写构造节点的方法createNode:parent,返回一个AVL树节点AVLNode,该节点继承自TreeNode

    3.1 AVLNode类
    • AVLNode.h
    /**
     AVL树节点
     */
    @interface AVLNode : TreeNode
    
    /** int - 高度*/
    @property(nonatomic,assign)int height;
    
    /** 平衡因子 */
    - (int)balanceFactor;
    
    /** 更新高度 */
    - (void)updateHeight;
    
    /** 返回节点数较多的节点 */
    - (TreeNode *)tallerChild;
    
    @end
    
    • AVLNode.m
    @implementation AVLNode
    
    /** 平衡因子 */
    - (int)balanceFactor {
        int leftHeight = self.left == nil ? 0 : ((AVLNode *)(self.left)).height;
        int rightHeight = self.right == nil ? 0 : ((AVLNode *)(self.right)).height;
        return leftHeight - rightHeight;
    }
    
    /** 更新高度 */
    - (void)updateHeight {
        int leftHeight = self.left == nil ? 0 : ((AVLNode *)(self.left)).height;
        int rightHeight = self.right == nil ? 0 : ((AVLNode *)(self.right)).height;
        
        self.height = 1 + MAX(leftHeight, rightHeight);
    }
    
    /** 返回节点数较多的节点 */
    - (TreeNode *)tallerChild {
        int leftHeight = self.left == nil ? 0 : ((AVLNode *)(self.left)).height;
        int rightHeight = self.right == nil ? 0 : ((AVLNode *)(self.right)).height;
        
        if (leftHeight > rightHeight) {
            return self.left;
        } else if (leftHeight < rightHeight) {
            return self.right;
        }
        
        return [self isLeftChild] ? self.left : self.right;
    }
    
    @end
    
    3.2 AVLTree类
    • AVLTree.h
    /**
     AVL树
     */
    @interface AVLTree : BST
    
    @end
    
    • AVLTree.m
    @implementation AVLTree
    
    /** 初始化 */
    - (AVLNode *)createNode:(id)element parent:(AVLNode *)parent {
        return [[AVLNode alloc] initWithElement:element parent:parent];
    }
    
    /// 新添加节点之后的处理
    - (void)afterAdd:(TreeNode *)node {
        while ((node = node.parent) != nil) {
            if ([self isBalanced:(AVLNode *)node]) {
                // 更新高度
                [self updateHeight:(AVLNode *)node];
            } else {
                // 恢复平衡
                [self rebalance:(AVLNode *)node];
                // 整颗树恢复平衡
                break;
            }
        }
    }
    
    /**
     恢复平衡
     @param grand 高度最低的那个不平衡节点
     */
    - (void)rebalance:(AVLNode *)grand {
        AVLNode *parent = (AVLNode *)(grand.tallerChild);
        TreeNode *node = parent.tallerChild;
        
        if (parent.isLeftChild) {   // L
            if (node.isLeftChild) { // LL
                [self rotateRight:grand];
            } else {    // LR
                [self rotateLeft:parent];
                [self rotateRight:grand];
            }
        } else {    // R
            if (node.isLeftChild) { // RL
                [self rotateRight:parent];
                [self rotateLeft:grand];
            } else {    // RR
                [self rotateLeft:grand];
            }
        }
    }
    
    /** 左旋转 grand - 爷爷节点 */
    - (void)rotateLeft:(AVLNode *)grand {
        TreeNode *parent = grand.right;
        TreeNode *child = parent.left;
        grand.right = child;
        parent.left = grand;
        [self afterRotate:grand parent:parent child:child];
    }
    
    /** 右旋转 */
    - (void)rotateRight:(AVLNode *)grand {
        TreeNode *parent = grand.left;
        TreeNode *child = parent.right;
        grand.left = child;
        parent.right = grand;
        [self afterRotate:grand parent:parent child:child];
    }
    
    /** 更新节点*/
    - (void)afterRotate:(TreeNode *)grand parent:(TreeNode *)parent child:(TreeNode *)child {
        // 让parent成为子树的根节点
        parent.parent = grand.parent;
    
        if (parent.isLeftChild) {
            grand.parent.left = parent;
        } else if (grand) {
            grand.parent.right = parent;
        } else {    // grand 是 root节点
            self.root = parent;
        }
        
        // 更新child的parent
        if (child != nil) {
            child.parent = grand;
        }
        
        // 更新grand的parent
        grand.parent = parent;
        
        // 更新高度
        [self updateHeight:(AVLNode *)grand];
        [self updateHeight:(AVLNode *)parent];
    }
    
    /** 是否是平衡树 */
    - (BOOL)isBalanced:(AVLNode *)node {
        return abs(node.balanceFactor) <= 1;
    }
    
    /** 更新子树高度 */
    - (void)updateHeight:(AVLNode *)node {
        [node updateHeight];
    }
    

    最后在二叉树BST添加节点的方法后需要平衡二叉树,即调用

    afterAdd: //方法 - 平衡二叉树
    
    3.3 测试代码
    // AVL树
    - (void)avlTreeTest {
        NSArray *datas = @[@67, @52, @92, @96, @53, @95, @13, @63, @34, @82, @76, @54, @9, @68, @39];
        
        AVLTree *avl = [[AVLTree alloc] init];
        for (int i = 0; i < datas.count; i++) {
            [avl add:datas[i]];
        }
        
        NSLog(@"%@",avl.description);
    }
    
    • 运行结果 - 添加节点后不平衡二叉树
    fe.png
    • 运行结果 - 添加节点后平衡二叉树


      image.png
    四 统一所有旋转操作
    image.png

    由上图可知,最终结果,中序排列顺序为 a,b,c,d,e,f,g

    代码实现如下

    • 平衡二叉树操作
    /**
     恢复平衡
     @param grand 高度最低的那个不平衡节点
     */
    - (void)rebalance2:(AVLNode *)grand {
        AVLNode *parent = (AVLNode *)grand.tallerChild;
        AVLNode *node = (AVLNode *)parent.tallerChild;
        
        if (parent.isLeftChild) {   // L
            if (node.isLeftChild) { // LL
                [self rotate:grand a:node.left b:node c:node.right d:parent e:parent.right f:grand g:grand.right];
            } else {    // LR
                [self rotate:grand a:parent.left b:parent c:node.left d:node e:node.right f:grand g:grand.right];
            }
        } else {    // R
            if (node.isLeftChild) { // RL
                [self rotate:grand a:grand.left b:grand c:node.left d:node e:node.right f:parent g:parent.right];
            } else {    // RR
                [self rotate:grand a:grand.left b:grand c:parent.left d:parent e:node.left f:node g:node.right];
            }
        }
    }
    
    • 旋转操作
    /// 旋转操作
    - (void)rotate:(TreeNode *)r    // 子树的根节点
                 a:(TreeNode *)a b:(TreeNode *)b c:(TreeNode *)c
                 d:(TreeNode *)d
                 e:(TreeNode *)e f:(TreeNode *)f g:(TreeNode *)g {
        // 让 d 成为这棵子树的根节点
        d.parent = r.parent;
        
        if (r.isLeftChild) {
            r.parent.left = d;
        } else if (r.isRightChild) {
            r.parent.right = d;
        } else {
            self.root = d;
        }
        
        // a-b-c
        b.left = a;
        if (a != nil) {
            a.parent = b;
        }
        b.right = c;
        if (c != nil) {
            c.parent = b;
        }
        [self updateHeight:b];
        
        // e-f-g
        f.left = e;
        if (e != nil) {
            e.parent = f;
        }
        f.right = g;
        if (g != nil) {
            g.parent = f;
        }
        [self updateHeight:f];
        
        // b-d-f
        d.left = b;
        d.right = f;
        b.parent = d;
        f.parent = d;
        [self updateHeight:d];
    }
    

    代码实现要结合上图一起看

    五 删除导致的失衡

    示例:删除下面这棵子树中的 16

    image.png
    • 只可能会导致父节点失衡
    • 除父节点以外的其他节点,都不可能失衡

    恢复平衡操作

    5.1 LL – 右旋转(单旋)
    • 如果绿色节点不存在,更高层的祖先节点可能也会失衡,需要再次恢复平衡,然后又可能导致更高层的祖先节点失衡...
    • 极端情况下,所有祖先节点都需要进行恢复平衡的操作,共 O(logn) 次调整
    调整前.png 调整后.png
    RR – 左旋转(单旋)
    调整前.png 调整后.png
    5.3 LR – RR左旋转,LL右旋转(双旋)
    调整前.png 调整后.png
    5.4 RL – LL右旋转,RR左旋转(双旋)
    调整前.png 调整后.png
    5.5 删除之后的恢复操作
    /** 删除节点后平衡二叉树 */
    - (void)afterRemove:(TreeNode *)node {
        while ((node = node.parent) != nil) {
            if ([self isBalanced:(AVLNode *)node]) {
                // 更新高度
                [self updateHeight:(AVLNode *)node];
            } else {
                // 恢复平衡
                [self rebalance:(AVLNode *)node];
            }
        }
    }
    
    六 总结
    6.1 添加
    • 可能会导致所有祖先节点都失衡
    • 只要让高度最低的失衡节点恢复平衡,整棵树就恢复平衡【仅需 O(1) 次调整】
    6.2 删除
    • 只可能会导致父节点失衡
    • 让父节点恢复平衡后,可能会导致更高层的祖先节点失衡【最多需要 O(logn) 次调整】
    6.3 平均时间复杂度
    • 搜索:O(logn)
    • 添加:O(logn),仅需 O(1) 次的旋转操作
    • 删除:O(logn),最多需要 O(logn) 次的旋转操作

    本文会持续更新中,更多精彩内容敬请期待。


    本文参考 MJ老师的 恋上数据结构与算法


    本人技术水平有限,如有错误欢迎指正。
    书写整理不易,您的打赏点赞是对我最大的支持和鼓励。


    项目连接链接 - 11_AVLTree

    相关文章

      网友评论

        本文标题:数据结构与算法之AVL树(九)

        本文链接:https://www.haomeiwen.com/subject/ptpizqtx.html