AVL树

5 篇文章 0 订阅

描述

AVL树是在儿叉搜索树的基础上衍生出来的数据结构。在儿叉搜索树中,高度取决于结点数据的大小的排序和插入顺序,在某些情况下,儿叉排序树的高度非常高,甚至退化成链表,使得其查找效率低下。因此,AVL树引入“平衡因子”的概念,平衡因子=左子树高度-右子树的高度。AVL树规定平衡因子的绝对值不大于1来保证二叉树的平衡。
在这里插入图片描述
关于二叉排序树的描述参考:点击跳转

操作流程

添加结点情况

添加一个结点对其父节点影响不会导致其失衡,而可能导致祖父结点失衡(可能所有祖父结点),因此只需让高度最低的失衡结点恢复平衡即可使得整棵树平衡,旋转操作次数为O(1)。我们对平衡二叉树失衡情况进行归类,可分为如下几种情况。

1、LL单旋
下图所示为LL失衡情况,失衡结点由g结点的left结点的left结点所导致(标红部分为新加入的结点),可通过获取左子树和右子树哪个高进行情况判定。
在这里插入图片描述
对此,对g结点进行右旋转使之平衡。主要操作伪代码:

g.left = p.right;
p.right = g;
p.parent = g.parent;
if g is left child then set p.parent.left = p else set p.parent.right = p;
g.parent = p;
g.left.parent = g;

2、LR双旋
下图所示LR失衡情况,失衡结点为g结点的left结点的right结点。在这里插入图片描述
双旋调整即对其进行两次调整。先将p结点左旋,得到LL情况,进而对g结点右旋。主要操作伪代码:

//先p左旋
p.right = n.left;
n.left = p;
n.parent = p.parent;
n.parent.left = n;
p.parent = n;
p.right.parent = p;
//后g右旋,操作流程同LL部分
g.left = p.right;
p.right = g;
p.parent = g.parent;
if g is left child then set p.parent.left = p else set p.parent.right = p;
g.parent = p;
g.left.parent = g;

3、RR单旋
RR单旋的情况和LL单旋的情况相反,只需对其进行相反的操作即可。
在这里插入图片描述
其主要操作伪代码:

g.right = p.left;
p.left = g;
p.parent = g.parent;
if g is left child then set p.parent.left = p else set p.parent.right = p;
g.parent = p;
g.right.parent = g;

4、RL双旋
RL双旋的情况和LR双旋的情况相反。
在这里插入图片描述

//先p左旋
p.left = n.right;
n.right = p;
n.parent = p.parent;
n.parent.right = n;
p.parent = n;
p.right.parent = p;
//后g右旋,操作流程同LL部分
g.right = p.left;
p.left = g;
p.parent = g.parent;
if g is left child then set p.parent.left = p else set p.parent.right = p;
g.parent = p;
g.right.parent = g;

删除结点的情况

删除结点可能导致父节点或祖父结点失衡(只有1各位结点会失衡),但对其恢复平衡后,可能使得子树高度降低,延续到祖先结点也可能产生失衡的情况,旋转操作次数为O(logn)。对删除结点进行归类,可分为如下几种情况。
1、LL单旋
在这里插入图片描述
删除T3下的某个节点,使得T3树高度-1,g结点失衡,g的左子树较高,通过对其子树的高度进行判定可知为LL情况,进而对其进行右旋转。主要旋转操作伪代码同添加的LR情况相同,但在旋转后,子树高度降低,可能导致祖父结点失衡,需要进一步对祖父结点进行失衡判定和调整。

LR双旋
同上,通过对其左右子树的高度进行判定为LR情况,进而对其旋转,操作类似于添加LR操作。
在这里插入图片描述
RR单旋
在这里插入图片描述
RL双旋
在这里插入图片描述

算法实现

我们对旋转操作进行抽象成一个类,以便后续为红黑树准备。

public abstract class BBSTree<E> extends BSTreeImpl<E>{
	public BBSTree(){
		this(null);
	}
	
	public BBSTree(Comparator<E> comparator){
		super(comparator);
	}
	
	/**
	 * 左旋
	 * @param node
	 */
	protected void rotateLeft(Node<E> grand){
		Node<E> parent = grand.right;
		Node<E> child = parent.left;
		grand.right = child;
		parent.left = grand;
		parent.parent = grand.parent;
		if(grand.isLeftChild()){
			grand.parent.left = parent;
		}else if(grand.isRightChild()){
			grand.parent.right = parent;
		}else{	//要删除的结点为根结点
			root = parent;
		}
		if(child != null)child.parent = grand;
		grand.parent = parent;
		
		afterRotateRight(grand, parent, child);
	}
	
	/**
	 * 右旋
	 * @param node
	 */
	protected void rotateRight(Node<E> grand){
		Node<E> parent = grand.left;
		Node<E> child = parent.right;
		parent.right = grand;
		grand.left = child;
		parent.parent = grand.parent;
		if(grand.isLeftChild()){
			grand.parent.left = parent;
		}else if(grand.isRightChild()){
			grand.parent.right = parent;
		}else{	//要删除的结点为根结点
			root = parent;
		}
		if(child != null) child.parent = grand;
		grand.parent = parent;
		afterRotateRight(grand, parent, child);
	}
	
	protected void afterRotateLeft(Node<E> grand,Node<E> parent,Node<E> child){}
	protected void afterRotateRight(Node<E> grand,Node<E> parent,Node<E> child){}
}

结点设计

对AVL树使用了平衡因子的概念,为了节省开销,不每次通过递归法获取高度,因此需要对其增加高度属性。

	protected static class AVLNode<E> extends Node<E>{
		public int height = 1;
		public AVLNode(E element, Node<E> parent) {
			super(element, parent);
		}
		/**
		 * 获取平衡因子 
		 * @return
		 */
		public int balanceFactor(){
			int leftHeight = left == null?0:((AVLNode<E>)left).height;
			int rightHeight = right == null?0:((AVLNode<E>)right).height;
			return leftHeight - rightHeight;
		}
		
		public void updateHeight(){
			int leftHeight = left == null?0:((AVLNode<E>)left).height;
			int rightHeight = right == null?0:((AVLNode<E>)right).height;
			this.height = (leftHeight > rightHeight?leftHeight:rightHeight) + 1;
		}
		
		/**
		 * 获取左右子树中高的结点
		 * @return
		 */
		public AVLNode<E> tallerChild(){
			int leftHeight = left == null?0:((AVLNode<E>)left).height;
			int rightHeight = right == null?0:((AVLNode<E>)right).height;
			return (AVLNode<E>)(leftHeight > rightHeight?left:right);
		}
	}

类设计

public class AVLTree<E> extends BBSTree<E>{	
	public AVLTree(){
		this(null);
	}
	
	public AVLTree(Comparator<E> comparator){
		super(comparator);
	}
	@Override
	protected Node<E> createNode(E element,Node<E> parent) {
		return new AVLNode<>(element, parent);
	}
	/**
	 * 是否平衡
	 * @param node
	 * @return
	 */
	private boolean isBanlance(Node<E> node){
		return Math.abs(((AVLNode<E>)node).balanceFactor()) <= 1;
	}
	/**
	 * 更新高度
	 * @param node
	 */
	private void updateHeight(Node<E> node){
		AVLNode<E> avlNode = (AVLNode<E>)node;
		avlNode.updateHeight();
	}
	/**
	 * 恢复平衡,不平衡的结点一定是添加后的祖先结点
	 * @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()){
			if(node.isLeftChild()){	//LL
				rotateRight(grand);
			}else{ //LR
				rotateLeft(parent);
				rotateRight(grand);
			}
		}else{
			if(node.isLeftChild()){ //RL
				rotateRight(parent);
				rotateLeft(grand);
			}else{ //RR
				rotateLeft(grand);
			}
		}
	}
}

添加结点

	@Override
	public void add(E element) {
		super.add(element);	
	}
	@Override
	protected void afterAdd(Node<E> node){
		while((node = node.parent) != null){
			if(isBanlance(node)){
				//更新高度
				updateHeight(node);
			}else{
				//恢复平衡
				rebalance(node);
				break;	//调整后直接break,调整后的子树和添加结点前子树高度不变, 因此上层结点不会失衡
			}
		}
	}

删除结点

	@Override
	public void remove(E element) {
		super.remove(element);
	}
	@Override
	protected void afterRemove(Node<E> node,Node<E> replacement){
		while((node = node.parent) != null){
			if(isBanlance(node)){
				updateHeight(node);
			}else{
				rebalance(node);	//这里不需要break,因为在调整后,可能会导致上层祖父结点失衡
			}
		}
	}

旋转后的操作

旋转后树高度有变化,因此需要对其进行调整。

	@Override
	protected void afterRotateLeft(Node<E> grand,Node<E> parent,Node<E> child) {
		updateHeight(grand);
		updateHeight(parent);
	}
	
	@Override
	protected void afterRotateRight(Node<E> grand,Node<E> parent,Node<E> child) {
		updateHeight(grand);
		updateHeight(parent);
	}

补充

对旋转操作进行汇总。在这里插入图片描述
发现旋转后的操作abcdefg子树按顺序排列,进而可对旋转操作进行统一。(另外一种旋转思路)

	@SuppressWarnings("unused")
	private void rebalance2(Node<E> grand){
		Node<E> parent = ((AVLNode<E>)grand).tallerChild();
		Node<E> node = ((AVLNode<E>)parent).tallerChild();	
		if(parent.isLeftChild()){
			if(node.isLeftChild()){	//LL
				rotate(grand, node.left, node, node.right, parent, parent.right, grand, grand.right);
			}else{ //LR
				rotate(grand, parent.left, parent, node.left, node, node.right, grand, grand.right);
			}
		}else{
			if(node.isLeftChild()){ //RL
				rotate(grand, grand.left, grand, node.left, node, node.right, parent, parent.right);
			}else{ //RR
				rotate(grand, grand.left, grand, parent.left, parent, node.left, node, node.right);
			}
		}
	}
	
	private void rotate(Node<E> r,		//子树的根结点
			Node<E> a,Node<E> b,Node<E> c,
			Node<E> d,
			Node<E> e,Node<E> f,Node<E> g){
		d.parent = r.parent;
		if(r.isLeftChild()){
			r.parent.left = d;
		}else if(r.isRightChild()){
			r.parent.right = d;
		}else{
			root = d;
		}
		
		b.left = a;
		if(a != null)a.parent = b;
		b.right = c;
		if(c != null)c.parent = b;
		updateHeight(b);
		
		f.left = e;
		if(e != null)a.parent = f;
		f.right = g;
		if(g != null)g.parent = f;
		updateHeight(f);	
		
		d.left = b;
		b.parent = d;
		d.right = f;
		f.parent = d;
		updateHeight(d);	
	}

完整代码

完整代码在我的Github:点击跳转

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值