JAVA实现AVL树的重构,解决因为添加和删除导致的失衡问题

一.为什么使用AVL树

二叉搜索树对节点的删除和添加都有可能让二叉树退化为链表,这样二叉搜索树的高度就变得很高,直接导致了搜索、添加、删除的时间复杂度变高。
所以我们考虑再添加删除后,使用尽量少的调整让树的高度变小,达到适度的平衡。进而提高删除,搜索,添加的效率。

二.AVL树特点

(1)每个节点的平衡因子只可能是-1,0,1(否则失衡)。

  • 平衡因子:某节点左右子树的高度差

(2)每个节点的左右子树高度差不能超过1。
(3)搜索、添加、删除的时间复杂度和高度差不多为 O(log n)。

三.内部类AVLNode

AVL树的节点比BST树的节点多了一个height属性。
求平衡因子的方法
思路:平衡因子是左右子树的高度差,使用三元运算符求左右子树高度:

		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;
			height = 1 + Math.max(leftHeight, rightHeight);
		}

求比较高的子节点
这个函数有什么作用,在afterAdd 函数设计中将会讲到。

		public Node<E> tallerChild(){
			int leftHeight = left == null ? 0 : ((AVLNode<E>)left).height;
			int rightHeight = right == null ? 0 : ((AVLNode<E>)right).height;
			if (leftHeight > rightHeight) {return left;}
			if (leftHeight < rightHeight) {return right;}
			//相等返回同边的
			return isLeftChild() ? left : right;
		}

四 .失衡问题的四种情况

1.失衡问题

本来这棵二叉树是平衡的,可是一旦添加了 13 节点,我们发现 14 ,15, 9节点全部都失衡了。一般来说这是最坏的情况,一直到根节点全部都失衡。
最好的情况是只有 14 一个节点失衡 或者 不失衡。
在这里插入图片描述

2.失衡问题的解决大体分为四种情况
(1)解决失衡:LL右旋转(单旋)

为什么叫LL呢,因为对于一个节点,导致他失衡的原因是,左孩子的左孩子添加一个元素。单旋意味着旋转一次。
在这里插入图片描述
如图我们看到,T0因为加了一个节点,导致grandparent失衡。这时我们的解决方法是右旋转:

g.left = p.right
p.right = g
p 成为此树的根节点。

旋转后结果
在这里插入图片描述
平衡达到了,而且还是一颗搜索二叉树,但是我们还要注意一下两点

① 改变T2,p,g的父节点
②更新 p ,g 的高度。
注意!!!平衡后树的高度较未添加红色节点前 没有变化

代码

	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 = parent;
		}
		//grand
		grand.parent = parent;
		//node 
		if (child != null) {
			child.parent = grand;
		}
		//更新高度
		updateHeight(grand);
		updateHeight(parent);
	}
(2)解决失衡:RR左旋转(单旋)

和LL右旋转 类似 RR左旋转 意味着,导致某个节点失衡的的原因是,它右孩子的右孩子添加了一个元素。
在这里插入图片描述

g.right = p.left
p.left = g
p 成为此树的根节点。
在这里插入图片描述
平衡达到了,而且还是一颗搜索二叉树,但是我们还要注意一下两点

① 改变T1,p,g的父节点
②更新 p ,g 的高度。
注意!!!平衡后树的高度较未添加红色节点前 没有变化

代码

	private void rotateRight(Node<E> grand) {
		Node<E> parent = grand.left;
		Node<E> child = parent.right;
		
		grand.left = child;
		parent.right = grand;
		
		parent.parent = grand.parent;
		if (grand.isLeftChild()) {
			grand.parent.left = parent;
		}else if (grand.isRightChild()) {
			grand.parent.right = parent;
		}else {
			root = parent;
		}
		
		grand.parent = parent;
		
		if (child != null) {
			child.parent = grand;
		}
		updateHeight(grand);
		updateHeight(parent);
		
	}
(3)解决失衡:LR 先左旋转再右旋转(双旋)

为什么叫LR呢,因为某个节点的左孩子的右孩子添加节点导致它的失衡。
双旋的意思时旋转两次
在这里插入图片描述

对 p 左旋转
在这里插入图片描述

对 g 右旋转

在这里插入图片描述
代码很简单

				rotateLeft(parent);
				rotateRight(grand);

注意!!!平衡后树的高度较未添加红色节点前 没有变化

(4)解决失衡:RL 先右旋转再左旋转(双旋)

这个大家可以参照LR

五 .afterAdd 函数设计

我们之前讲到,添加导致失衡的最坏情况是所有的祖父节点都失衡,那是不是所有的失衡节点都要去去一个一个平衡呢?

并不是这样的.只要让高度最低的失衡节点恢复平衡,整棵树就可以恢复平和。(仅需要调整O(1)次),
而且失衡结点恢复平衡后,结点的高度和未添加结点平衡时的高度一样。这意味这,我们更新结点的高度这个操作到最低的失衡结点处就可以停止了。

1. 设计思路

因为只要平衡最低的失衡节点,树就可以恢复平衡,所以在恢复平衡函数rebalance(node); 后直接break 退出循环。
这是一种设计的思路:我们先把逻辑写下来,差什么函数,写完逻辑后再补。比如 rebalance(node); 现在只有逻辑意义:平衡一个节点。但是没有实现的代码。

	protected void afterAdd(Node<E> node) {
			while((node = node.parent) != null) {
				if (isBalanced(node)) {
					//更新高度
					updateHeight(node);
				}else {
					//恢复平衡
					rebalance(node);
					break;
				}
			}
	}
2.恢复平衡 rebalance 方法设计

我们已经了解到了,失衡的四种情况,现在我们可以把四种情况整合一下,写一个无论哪种情况都能恢复平衡的 rebalance 函数,需要注意的是我们这个函数需要传入的是失衡节点,我们上述四种情况都是grandparent(也就是g节点)失衡。所以我们我们传入的型参就写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.isRightChild()) {//RR
				rotateLeft(grand);
			}else {//RL
				rotateRight(parent);
				rotateLeft(grand);
			}
		}
	}
3.tallerChild 函数的设计意义

大家可以看到,AVLNode中有一个tallerChild 函数。而且我在rebalance中也用到了。为什么要写这个函数呢?

在这里插入图片描述

大家看这个图,我传入rebalance的是 g 节点。我们看这个图得以知道,导致他失衡的是他左孩子的左孩子。但是没图的话,用代码如何去找是哪个子节点的 子节点让他失衡呢?


g节点失衡,导致他失衡的节点一定在他左右子树中高度最高的子树中,所以我们编写tallerchild 函数 ,来找高度最高的子树

五 .删除导致的失衡问题,afterRemove函数设计

首先你需要知道的是
1 删除节点只可能导致父节结点失衡(有且只有一个节点失衡)
为什么呢?在删除导致失衡的情况下,被删除的结点不会改变失衡点的高度。所以祖先结点的平衡因子不会发生改变
2 让父节点恢复平衡之后可能会导致更高层的祖先节点失衡(最多需要O(logn)次调整)

1.afterRemove函数设计

我们根据上述的绿字来设计afterRemove函数。

	protected void afterRemove(Node<E> node) {
		while((node = node.parent) != null) {
			if (isBalanced(node)) {
				//更新高度
				updateHeight(node);
			}else {
				//恢复平衡
				rebalance(node);
			}
		}
	}

afterRemove和afterAdd的区别在于,比如删除结点的父节点失衡,我们平衡了父节点之后,祖父结点也有可能失衡,所以我们要一直循环,直到根结点。
相同点在于,更新高度这个操作都到最低的失衡节点处(删除只导致一个失衡节点,我们把它看做最低),但是他们的原因不同:
afterRemove是因为删除不影响失衡节点以上(包括失衡节点)的祖先节点高度。
afterAdd是因为失衡结点恢复平衡后,结点的高度和未添加结点平衡时的高度一样。即添加影响高度,但是平衡后恢复原先高度

重构代码

AVL树是BST(二叉搜索树)的子类,BST代码在之前的文章中

import java.util.Comparator;

public class AVLTree<E> extends BST<E>{
	
	
	//构造方法
	public AVLTree(){
		this(null);
	}
	
	public AVLTree(Comparator<E> comparator) {
		super(comparator);
	}
	
	
	/*
	 一棵树是否平衡
	 */
	private boolean isBalanced(Node<E> node) {
		return Math.abs(((AVLNode<E>)node).balanceFactor()) <= 1;
	}
	
	/*
	 高度更新方法的封装
	 */
	private void updateHeight(Node<E> node) {
		((AVLNode<E>)node).updateHeight();
	}
	
	/*
	 AVL数需要有高度属性
	 AVL特有的节点
	 */
	private static class AVLNode<E> extends Node<E>{
		int height = 1;
		
		public AVLNode(E element, Node<E> parent) {
			super(element, parent);
			// TODO 自动生成的构造函数存根
		}
		
		/*
		 求平衡因子
		 */
		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;
			height = 1 + Math.max(leftHeight, rightHeight);
		}
		/*
		 * 找比较高的子节点
		 */
		public Node<E> tallerChild(){
			int leftHeight = left == null ? 0 : ((AVLNode<E>)left).height;
			int rightHeight = right == null ? 0 : ((AVLNode<E>)right).height;
			if (leftHeight > rightHeight) {return left;}
			if (leftHeight < rightHeight) {return right;}
			//相等返回同边的
			return isLeftChild() ? left : right;
		}
	}
	
	

	
	
	
	/*
	 * 重写方法恢复平衡的逻辑
	 */
	@Override
	protected void afterAdd(Node<E> node) {
			while((node = node.parent) != null) {
				if (isBalanced(node)) {
					//更新高度
					updateHeight(node);
				}else {
					//恢复平衡
					rebalance(node);
					break;
				}
			}
	}
	
	
	/*
	 删除后恢复平衡的方法
	 */
	@Override
	protected void afterRemove(Node<E> node) {
		while((node = node.parent) != null) {
			if (isBalanced(node)) {
				//更新高度
				updateHeight(node);
			}else {
				//恢复平衡
				rebalance(node);
			}
		}
	}
	
	
	
	/*
	 恢复树平衡的逻辑
	 */
	
	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.isRightChild()) {//RR
				rotateLeft(grand);
			}else {//RL
				rotateRight(parent);
				rotateLeft(grand);
			}
		}
	}
	
	
	/*
	 统一的恢复平衡的方法
	 */
	private void rebalance2(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
				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 { //R
			if (node.isRightChild()) {//RR
				rotate(grand, grand.left, grand, parent, parent.left, node, node.left, node.right);
			}else {//RL
				rotate(grand, grand.left, grand, node.left, node, node.right, parent, parent.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成为这个子树的根节点
		d.parent = r.parent;
		if (r.isLeftChild()) {
			r.parent.left = d;
		}else if (r.isRightChild()) {
			r.parent.right = d;
		}else {
			//根节点
			root = d;
		}
		
		// a-b-c
		b.left = a;
		b.right = c;
		if (a != null) {
			a.parent = b;
		}
		if (c != null) {
			c.parent = b;
		}
		updateHeight(b);
		//e-f-g
		f.left = e;
		f.left = g;
		if (e != null) {
			a.parent = f;
		}
		if (g != null) {
			g.parent = g;
		}
		updateHeight(f);
		
		//b-d-f
		d.left = d;
		d.left = f;
		b.parent = d;
		f.parent = d;
		updateHeight(d);
	}
	
	
	
	/*
	 左旋转
	 */
	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 = parent;
		}
		//grand
		grand.parent = parent;
		//node 
		if (child != null) {
			child.parent = grand;
		}
		//更新高度
		updateHeight(grand);
		updateHeight(parent);
	}
	/*
	 右旋转
	 */
	private void rotateRight(Node<E> grand) {
		Node<E> parent = grand.left;
		Node<E> child = parent.right;
		
		grand.left = child;
		parent.right = grand;
		
		parent.parent = grand.parent;
		if (grand.isLeftChild()) {
			grand.parent.left = parent;
		}else if (grand.isRightChild()) {
			grand.parent.right = parent;
		}else {
			root = parent;
		}
		
		grand.parent = parent;
		
		if (child != null) {
			child.parent = grand;
		}
		updateHeight(grand);
		updateHeight(parent);
		
	}
	
	/*
	 重写方法 创建节点
	 */
	@Override
	protected Node<E> createNode(E element, Node<E> parent) {
		return new AVLNode<E>(element, parent);
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值