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)次的旋转操作
posted @ 2021-03-26 10:08  codeFiler  阅读(162)  评论(0编辑  收藏  举报