zoukankan      html  css  js  c++  java
  • AVL树问题

    06_AVL树

    1、AVL树

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

    2、平衡对比

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

    5

    3、简单的继承结构

    Snipaste_2021-03-24_09-06-37

    3、AVL添加

    3.1、添加导致的失衡

    • 实例:往下面这棵子树中添加13
    • 最坏情况:可能会导致所有祖先节点都失衡
    • 父节点、非祖先节点,都不可能失衡

    比如下图所示:

    ​ 在这个树中添加元素13,那么会使父节点14失衡,祖父节点15和9都会失衡

    Snipaste_2021-03-24_09-19-05

    3.1.1、LL—右旋转(单旋)

    如下图所示:

    ​ 我们在T0这个节点处添加一个节点的话,就会使g这个祖父节点失衡,由于n这个节点是g节点的左节点的左节点,即n = g.left.left,所以为LL,如果我们要使它变为一棵平衡二叉树,我们就需要将g节点向右旋转使他变为p节点的子节点

    ​ 也就是我们要做的操作就是:

    - g.left = p.right
    - p.right = g
    

    ​ 然后我们要使p节点作为这个子树的根节点

    ​ 同时我们还需要注意的是:我们还必须维护T2、p、g的parent属性,然后更新g,p的高度

    Snipaste_2021-03-24_09-48-57

    3.1.2、RR—左旋转(单旋)

    如下图所示:

    ​ 我们要在T3节点处插入一个节点,那么就会导致祖父节点g失衡,由于n节点是祖父节点的右节点的右节点,也即n = g.right.right,所以为RR。我们要解决这个问题,只需要将这个树的g节点进行左旋就可以。

    操作如下:

    - g.right = p.right
    - p.left = g
    

    让p成为这棵子树的根节点

    同时我们还需要注意的是:我们还得维护T1,p,g的parent属性,同时得先后更新g,p的高度。

    Snipaste_2021-03-24_09-49-11

    3.1.3、LR—RR左旋转,LL右旋转(双旋)

    如下图所示:

    ​ 我们要在T2节点处添加新的节点,那么同样会导致g节点失衡,由于n = g.left.right,所以为LR,解决这种问题我们只要先对p节点进行左旋,然后对g节点进行右旋就可以解决问题

    Snipaste_2021-03-24_09-49-39

    3.1.4、RL—LL右旋转,RR左旋转(双旋)

    如下图所示:

    ​ 我们要在T1节点处添加新的节点,那么同样会导致g节点失衡,由于n = g.right.left,所以为RL,解决这种问题我们只要先对p节点进行右旋,然后对g节点进行左旋就可以解决问题

    Snipaste_2021-03-24_15-47-53

    如下即为左旋的代码:

    /**
     * 左旋转
    */
    private void rotateLeft(Node<E> grand){
    	Node<E> parent = grand.right;
    	Node<E> child = parent.left;
    	grand.right = child;
        parent.left = grand;
    	
    	// 让parent成为子树的根节点
    	parent.parent = grand.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;
    			
    	// 更新高度
    	updateHeight(grand);
    	updateHeight(parent);
    }
    

    这里面的更新操作的代码其实很简单,只需要

    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);
    }
    

    同样的其实右旋转也很简单:

    /**
     * 右旋转
    */
    private void rotateLeft(Node<E> grand){
    	Node<E> parent = grand.left;
    	Node<E> child = parent.right;
    	grand.left = child;
    	parent.right = grand;
    	
    	// 让parent成为子树的根节点
    	parent.parent = grand.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;
    			
    	// 更新高度
    	updateHeight(grand);
    	updateHeight(parent);
    }
    

    4、AVL删除

    4.1、删除导致的失衡

    • 示例:删除子树中的16
    • 可能会导致父节点或祖先节点失衡(只有1个节点会失衡),其他节点,都不可能失衡

    如下图所示:我们要删除节点16的话,就会导致父节点失衡

    Snipaste_2021-03-24_17-47-54

    如下图所示:

    ​ 我们要删除节点16的时候,虽然15节点不会失衡,但是节点11会失衡

    image-20210324193515985

    4.1.1、LL—右旋转(单旋)

    • 如果绿色节点不存在,更高层的祖先节点可能也会失衡,需要再次恢复平衡,然后又可能导致更高层的祖先节点失衡..
    • 极端情况下,所有祖先节点都需要进行恢复平衡,功O(logn)次调整

    image-20210324194038837

    4.1.2、RR—左旋转(单旋)

    image-20210324194521561

    4.1.3、LR—RR左旋转,LL右旋转(双旋)

    image-20210324194921250

    4.1.4、RL—LL右旋转,RR左旋转(双旋)

    image-20210324195359579

    5、总结

    • 添加
      • 可能会导致所有祖先节点都失衡
      • 只要让高度最低的失衡节点恢复平衡;整棵树就恢复平衡(仅需要O(1)次调整)
    • 删除
      • 可能会导致父节点或祖先节点失衡(只有1个节点会失衡)
      • 恢复平衡后,可能会导致更高层的祖先节点失衡(最多需要O(logn)次调整)
    • 平均时间复杂度
      • 搜索:O(logn)
      • 添加:O(logn),仅需O(1)次的旋转操作
      • 删除:O(logn),最多需要O(logn)次的旋转操作
  • 相关阅读:
    docker 简单使用
    apache 目录网站显示indexs
    MySQL索引失效的几种情况
    mysql 基本常用语句
    UNIX 版本
    B语言的发明者 Ken Thomson & C语言的发明者Dennis Ritchie
    My SQl 积累
    C# DGV多行选择
    C#中很模糊查询DGV中数据的两种方法
    网址
  • 原文地址:https://www.cnblogs.com/coderD/p/14580755.html
Copyright © 2011-2022 走看看