二叉搜索树(2)之AVL树 笔记

AVL树的特点

  1. 每个节点的平衡因子只可能是 -1、 0、 1 (绝对值 < 1,如果大于一,则称之失衡)
  2. 每个节点的左右两端的高度差不超过1
  3. 添加 搜索 删除 的时间复杂度是O(log n)
  • 平衡因子:某节点的左右子树的高度差
  • AVL树:被称之为 平衡二叉搜索树

添加

  • 在二叉搜索树节点添加之后,维护该二叉树遵守AVL树的性质(AVL树继承二叉搜索树)
    protected void afterAdd(Node<E> node) {
        while ((node = node.parent) != null) {
            if (isBalanced(node)) {
                // 更新高度
                ((AVLNode<E>)node).updateHeight();
            } else {
                // 恢复平衡
                rebalance(node);
                // 整棵树恢复平衡
                break;
            }
        }
    }
  • 循环查看 被添加节点所有祖先节点的的平衡因子 (从被添加节点的父节点开始),如果没有失衡就更新该节点的高度,失衡了就回复平衡。(我的理解是 被添加节点的父节点都不会失衡 跟新 父节点高度之后 从祖父节点开始都可能失衡)

更新节点高度

  • 该方法在节点类里边
    public void updateHeight() {
        int leftHeight = left == null ? 0 : ((AVLNode<E>)left).height;
        int rightHeight = right == null ? 0 : ((AVLNode<E>)right).height;
        height = 1 + Math.max(leftHeight, rightHeight);
    }
  • 取左右子树高度的最大值 加一 就是当前节点的高度

恢复平衡

    /**
     * 恢复平衡
     * @param grand 高度最低的那个不平衡节点
     */
    private void rebalance(Node<E> grand) {
        //获取失衡节点的儿子节点(左右子节点比较高的那个)
        Node<E> parent = ((AVLNode<E>)grand).tallerChild();
        //获取失衡节点的孙子节点
        Node<E> node = ((AVLNode<E>)parent).tallerChild();
        if (parent.isLeftChild()) { // L
            if (node.isLeftChild()) { // LL
                rotateRight(grand);
            } else { // LR
                rotateLeft(parent);
                rotateRight(grand);
            }
        } else { // R
            if (node.isLeftChild()) { // RL
                rotateRight(parent);
                rotateLeft(grand);
            } else { // RR
                rotateLeft(grand);
            }
        }
    }
  1. parent节点在grand节点的left边 node节点在parent 的left边 只需要对 grand 进行 right旋转,即可恢复该失衡节点的平衡
  2. parent节点在grand节点的left边 node节点在parent 的rigth边 则需要先对parent进行left 旋转 grand进行right旋转,即可恢复该失衡节点的平衡
  3. parent节点在grand节点的right边 node节点在parent 的right边 只需要对 grand 进行 left旋转,即可恢复该失衡节点的平衡
  4. parent节点在grand节点的right边 node节点在parent 的left边 则需要先对parent进行right 旋转 grand进行left旋转,即可恢复该失衡节点的平衡

旋转

  • 左旋转
    protected void rotateLeft(Node<E> grand) {
        //对谁进行左旋转 谁就被认为是祖父节点
        //找到 parent 节点
        Node<E> parent = grand.right;
        //找到child 节点
        Node<E> child = parent.left;
        
        //左旋转时 
        // grang  下来  pareng.left = grand; 
        // grang.right = child
        grand.right = child;
        parent.left = grand;
        // 改变了 树中的 两条线 所以得维护三个节点的父节点属性 与 原来指向grang 改为指向 parent
        afterRotate(grand, parent, child);
    }
  • 右旋转
    protected void rotateRight(Node<E> grand) {
        Node<E> parent = grand.left;
        Node<E> child = parent.right;
        grand.left = child;
        parent.right = grand;
        afterRotate(grand, parent, child);
    }
  • 维护三个节点的父节点属性
    protected void afterRotate(Node<E> grand, Node<E> parent, Node<E> child) {
        // 让parent称为子树的根节点
        parent.parent = grand.parent;
        //原来指向garent的那条线 指向 parent
        if (grand.isLeftChild()) {
            grand.parent.left = parent;
        } else if (grand.isRightChild()) {
            grand.parent.right = parent;
        } else { // grand是root节点
            root = parent;
        }
        
        // 更新child的parent
        if (child != null) {
            child.parent = grand;
        }
        
        // 更新grand的parent
        grand.parent = parent;
    }

删除

    protected void afterRemove(Node<E> node) {
        while ((node = node.parent) != null) {
            if (isBalanced(node)) {
                // 更新高度
                updateHeight(node);
            } else {
                // 恢复平衡
                rebalance(node);
            }
        }
    }
  • 删除逻辑和添加相似 从被删除节点开始 往上查失衡节点 恢复平衡
  • 真正删除的节点都是 度 为 1或0 的节点
  • AVL树被删除的节点 都是在倒数两层内的 要么是 叶子节点 要么事 倒数第二层度为一的节点
  • 在二叉树删除中 删除度为1的节点时 afterRemove(node) node 传的是用以替换被节点的节点 (这个在二叉搜索树的删除方法里能看到)(不影响AVL 树的删除 是为了红黑树删除搞得 因为 AVL树 就是从删除节点往上找失衡节点恢复平衡的)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358