java实现 AVLTree 或 AVL树 ,包含源码、注释、说明

这篇博客介绍了AVL树的实现,它是二叉平衡树的一种,保证任意节点的平衡因子绝对值不大于1。文章详细讲解了AVL树在添加和删除节点后的平衡恢复操作,包括`addAfterOpt`和`removeAfterOpt`方法,以及如何通过旋转操作恢复平衡。此外,还讨论了自定义节点类和多态性的应用。
摘要由CSDN通过智能技术生成

温馨提示
该AVLTree继承自我之前写的BinarySearchTree(二叉平衡树)【点击这里查看】,所以没有看我之前的的BinarySearchTree(二叉平衡树)【点击这里查看】)的小伙伴需要去看看,要不然有些地方你可能会看不懂哦。

说明:

每个方法的作用,在源码的注释中,我已经写的非常详细了,这里就略过了。在这里我主要想要介绍VALTree中一些特有的方法,或者说重写的方法

  1. 第一个就是再添加节点之后的操作,封装成addAfterOpt(Node node)方法
    在这里插入图片描述
  2. 第二个就是再删除节点之后的操作,封装成removeAfterOpt(Node node)方法。
    该方法与addAfterOpt(Node)的唯一的区别就是不要“break”。
    因为node节点被删除之后,只会导致其父节点或祖先节点中的一个失去平衡,将失衡的节点进行翻转之后,失衡节点回到平衡
    但是有可能性导致恢复平衡的节点上面的节点失去平衡,最坏的可能性会导致node节点的parents与
    在这里插入图片描述
  3. 第三个是创建一个节点的方法,CreatNewNode(E element, Node parentNode)
    如果说,其他的二叉树(A)继承了BinarySearchTree,那么A中的Node可能会有其他的一些元素和方法,所以A新创建的node
    就不能是该类中的node,要是A自己的node
    所以只要A重写该方法,返回自己的node就可以了【因为该方法的返回值是Node,所以A重写后的方法的返回值也是Node,因此A自己的ANode需要继承该类中的Node,这样A即使返回自己的ANode也不会报错(用到的思想是’多态性’)】。
    这里的AVLTree就是上面说的A
    在这里插入图片描述
  4. 第四个是定义自己的Node类,但是该类要继承BinarySearchTree中Node。因为其他的树中的节点可能会有自己特有的属性和方法
    在这里插入图片描述
    ① 特有的属性:
    在这里插入图片描述
    ② 特有的方法:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

源码:

package algorithmBeauty.dataStructure.tree.avlTree;

import java.util.Comparator;

import algorithmBeauty.dataStructure.tree.binarySearchTree.BinarySearchTree;

/**
 * @author : HuXuehao
 * @Description AVLTree(二叉平衡树)继承 BinarySearchTree(二叉搜索树)
   AVLTree是平衡二叉树的一种,它的平衡条件是平衡因子的绝对值不大于1
   
   AVLTree的特点:
   1.AVLTree 是二叉搜索树
   2.AVLTree的任意一个节点的平衡因子的绝对值不大于1
   	  平衡因子:左子树的高度 - 右子树的高度
*/
@SuppressWarnings("unchecked")
public class AVLTree<E> extends BinarySearchTree<E> {
	/**
	 * @Description 没有比较器说明 作为二叉树的节点 的对象已经实现了Comparable接口,
	 * 并重写了compareTo()方法
	 */
	public AVLTree() {
		this(null); //调用有参的构造方法,并且参数是null
	}
	
	/**
	 * @Description 说明你在创建对象的时候,就要给我传一个比较器,自定义比较的逻辑
	 */
	public AVLTree(Comparator<E> comparator) {
		super(comparator);
	}
	
	/*
	 * 增加父节点导致二叉树失去平衡,而且失去平衡的节点一定是在node的parent节点到root节
	 * 点的连线上,最坏的是导致node的所有的祖先节点失衡,使的整个二叉树恢复平衡的做法是:找
	 * 到距离node的parent节点最近的一个失去平衡的节点,将其翻转,将会整个二叉树恢复平衡。
	 */
	@Override
	protected void addAfterOpt(Node<E> node) {
		
		while((node = node.parent) != null) {
			if(isBalance(node)) {
				//更新高度
				updataNodeHeight(node);
			}else {
				//恢复平衡(该节点恢复平衡在之后,整个二叉树恢复平衡,停止遍历)
				recoverBalance(node);
				break;
			}
		}
	}
	
	/*
	 * 该方法与addAfterOpt(Node)的唯一的区别就是不要“break”。
	 * 因为node节点被删除之后,只会导致其父节点或祖先节点中的一个失去平衡,将失衡的节点进行翻转之后,失衡节点回到平衡,
	 * 但是有可能性导致恢复平衡的节点上面的节点失去平衡,最坏的可能性会导致node节点的parents与
	 * root节点连线上的所有的节点都会失去平衡,所以需要遍历到根节点,来判断相关节点是否失去平衡
	 * 
	 * 注意:node被删除之后,调用的 removeAfterOpt(Node<E> node),那么在该方法中我使用
	 * node.parent还是node的parent吗? 答案是肯定的,如果你去BinarySearchTree的remove() 
	 * 的方法中看一看node.parent一直没有被操作,被操作的是node.parent.left或node.parent.
	 * right,所以node.parent一直还在。
	 */
	@Override
	protected void removeAfterOpt(Node<E> node) {
		
		while((node = node.parent) != null) {
			if(isBalance(node)) {
				//更新高度
				updataNodeHeight(node);
			}else {
				//恢复平衡
				recoverBalance(node);
			}
		}
	}
	
	/**
	 * @Description 创建一个新节点
	 * 如果说,其他的二叉树(A)继承了该类,那么A中的Node可能会有其他的一些元素和方法,所以A新创建的node
	 * 就不能是该类中的node,要是A自己的node。所以只要A重写该方法,返回自己的node就可以了【因为该方法的返回值是Node<E>,
	 * 所以A重写后的方法的返回值也是Node<E>,因此A自己的ANode<E>需要继承该类中的Node,这样A即使返回自己的ANode<E>也不会报错(用到的思想是'多态性')】
	 * 
	 * 这里的AVLTree就是上面说的A
	 */
	@Override
	protected Node<E> CreatNewNode(E element, Node<E> parentNode) {
		return new AVLNode<>(element, parentNode);
	}
	
	/**
	 * @Description 恢复平衡
	 * @param grandparent 不平衡且高度最低的节点
	 */
	private void recoverBalance(Node<E> grandparent) {
		/* 对于不平衡节点grandparent而言,parent、node有以下特点
		 * parent 插入元素的父节点 :grandparent的 较高 子树的根节点
		 * node 插入元素节点 parent的 较高 子树的根节点
		 */
		Node<E> parent  = ((AVLNode<E>)grandparent).tellerChild();
		Node<E> node  = ((AVLNode<E>)parent).tellerChild();
		
		//判断方向
		String dir = judgeDirection(grandparent,parent,node);
		
		//进行翻转
		if("LL".equals(dir)) {
			rotateRright(grandparent);
			
		}else if("RR".equals(dir)) {
			rotateLeft(grandparent);
			
		}else if("LR".equals(dir)) {
			rotateLeft(parent);
			rotateRright(grandparent);
			
		}else { //RL
			rotateRright(parent);
			rotateLeft(grandparent);
			
		}
	}
	
	/*
	 * 对node节点进行右旋转
	 */
	
	private void rotateRright(Node node) {
		Node<E> tempNode = node.left; //备份目前的node的左节点left
		
		node.left = tempNode.right; //将node是左指针指向node的左节点的右子树
		if(node.left != null) node.left.parent = node; //更新父节点
		
		tempNode.right = node; //将tempNode的右指针指向node
		tempNode.parent = node.parent; //更新父节点
		
		//取代node的位置
		if(node.parent.left == node) { //node是其父节点的left时
			node.parent.left = tempNode;
		}else if(node.parent.right == node){//node是其父节点的right时
			node.parent.right = tempNode;
		}else { //node是根节点(root)
			root = tempNode;
		}
		
		node.parent = tempNode; //更新node的父节点
		
		//更新高度
		updataNodeHeight(tempNode);
		updataNodeHeight(node);
	}
	
	/*
	 * 对node节点进行左旋转
	 */
	private void rotateLeft(Node node) {
		Node<E> tempNode = node.right; //备份目前的node的左节点left
		
		node.right = tempNode.left; //将node是左指针指向node的左节点的右子树
		if(node.right != null) node.right.parent = node; //更新父节点
		
		tempNode.left = node; //将tempNode的右指针指向node
		tempNode.parent = node.parent; //更新父节点
		
		//取代node的位置
		if(node.parent.left == node) { //node是其父节点的left时
			node.parent.left = tempNode;
		}else if(node.parent.right == node){//node是其父节点的right时
			node.parent.right = tempNode;
		}else { //node是根节点(root)
			root = tempNode;
		}
		
		node.parent = tempNode; //更新node的父节点
		
		//更新高度
		updataNodeHeight(tempNode);
		updataNodeHeight(node);
	}
	/**
	 * 
	 * @Description 判断parent是grandparent的那个节点,node是parent的那个节点
	 * @param grandparent 高度最低不平衡节点(插入元素的父节点的父节点)
	 * @param parent 插入元素的父节点
	 * @param node 插入元素节点
	 * @return 判断的结果
	 */
	private String judgeDirection(Node<E> grandparent, Node<E> parent, Node<E> node) {
		if(grandparent.left == parent && parent.left == node) return "LL";
		if(grandparent.right == parent && parent.right == node) return "RR";
		if(grandparent.left == parent && parent.right == node) return "LR";
		return "RL";
	}

	/**
	 * @Description 更新节点的高度
	 * @param node 
	 */
	private void updataNodeHeight(Node<E> node) {
		((AVLNode<E>)node).updataSelfHeight();
	}
	
	/**
	 * @Description 判断该节点是否平衡
	 * @param node 
	 */
	private boolean isBalance(Node<E> node) {
		//abs 取绝对值
		return Math.abs(((AVLNode<E>)node).balanceFactor()) <= 1;
	}
		
	//定义AVLTree特有的Node,并继承BinarySearchTree中的Node
	private class AVLNode<E> extends Node<E> {
		int height = 1; //每个节点被创建之后节点高度默认为1
		
		public AVLNode(E element, Node<E> parent) {
			super(element, parent);
		}
		
		//获取该节点的平衡因子
		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 updataSelfHeight() {
			int leftHeight = left == null ? 0 : ((AVLNode<E>)left).height;
			int rightHeight = right == null ? 0 : ((AVLNode<E>)right).height;
			height = Math.max(leftHeight, rightHeight) + 1;
		}
		
		//返回当前节点比较高的子节点
		public Node<E> tellerChild(){
			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 this == this.parent.left? left : right;
		}
	}
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值