AVLTree节点失衡处理java代码

以下代码和实现思路来源于B站视频“恋上数据结构与算法”,地址: https://www.bilibili.com/video/BV13v41167ix?

前期准备

BinaryTree的Node节点类中添加两个方法,用于判断当前节点为其父节点的左孩子节点或右孩子节点

	//判断某个节点为父节点的左子节点
			public boolean isLeftChild() {
				return parent!=null&&parent.left==this;	
			}
			public boolean isRightChild() {
				return parent!=null&&parent.right==this;	
			}

AVLTree继承自二叉搜索树,二叉搜索树原来的add方法中对添加的元素按照一定逻辑添加,但并没有对元素失去平衡进行判断和处理。所以在BinarySearchTree中添加一个AddAter方法,用于对节点添加后进行失衡判断和失衡处理。

	//交给AVLTree类去实现的方法,在普通二叉树中没有函数体
		protected void AfterAdd(Node<E> node) {
			
		}
public void add(E element) {
		elementNotNullCheck(element);//先检查元素是否为空
		//当root节点为空时
		if(root==null) {
			root=CreateNode(element,null);//此处不加<>会出现警告
			size++;
			AfterAdd(root);//调用函数对添加后的节点进行失衡处理
			return;
		}
		
		int cmp = 0;//定义为0避免警告
		Node<E> node=root;
		Node<E> parent = null;//初始为空避免警告
		//找到父节点
		while(node!=null) {
			
			parent=node;//保存父节点
			cmp=compare(element, node.element);
			
			if(cmp>0) {//如果待插入元素较大
				node=node.right;
			}else if(cmp<0) {//如果待插入元素较小
				node=node.left;
			}else {//如果相等,覆盖
				node.element=element;
				return;
			}
		}
		//将当前元素插入到父节点的指定位置
		Node<E> newNode= CreateNode(element, parent);
		if(cmp>0) {
			parent.right= newNode;
		}
		if(cmp<0) {
			parent.left= newNode;
		}
		size++;
		AfterAdd(newNode);//调用函数对添加后的节点进行失衡处理
	}

在AVLTree中重新定义一个AVLNode类,它添加一个height属性

	private static class AVLNode<E> extends Node<E>{
		//height默认值为1,叶子节点的高度为1 
		int height=1;
		
		public AVLNode(E element, Node<E> parent) {
			super(element, parent);
		}
	}

失衡判断与处理

分析可知,在对AfterAdd方法的函数体进行编写时需要有更新节点高度的方法,和求平衡因子的方法。两个方法都写在AVLNode中,便于方法height属性

public void UpdateHeight(Node<E> node){
	int leftHeight=left==null?0:((AVLNode<E>)left).height;
	int rightHeight=height==null?((AVLNode<E>)right).height;
	return 1+Math.max(leftHeight,rightHeight);
}
public int BalanceFactor(Node<E> node){
	int leftHeight=left==null?0:((AVLNode<E>)left).height;
	int rightHeight=height==null?((AVLNode<E>)right).height;
	return leftHeight-rightHeight;
}

在此基础上,可以很容易地在AVLTree类中写出判断节点是否平衡的方法和更新节点高度的方法

private void updateHeight(Node<E> node){
	((AVLNode<E>)node).UpdateHeight;
}
private boolean isBalance(Node<E> node){
	return Maht.abs(BalanceFanctor(node))<=1;
}

于是在AddAfter方法中就可以写出框架

	protected void AfterAdd(Node<E> node) {
		 while((node=node.parent)!=null) {
			 
			 if(isBalanced(node)) {//如果该节点是平衡的
				 updateHeight(node);//更新高度
			 }else {
				 //恢复平衡
				 reBalance(node);
				 break;
			 } 
		 }
	}
恢复平衡

现在的主要工作就是编写恢复平衡方法,在恢复平衡方法中,需要判断节点失衡类型。在判断失衡类型的时候,需要知道是左子节点还是右子节点导致的失衡,分析可知,如果节点失衡,那么一定是由其孩子节点中较高的子树导致的,所以可以在AVLNode中编写一个求较高孩子节点的方法:

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

如果定义好左旋方法(rotateLeft)和右旋方法(rotateRight)后,恢复平衡的代码框架如下:

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

左旋和右旋方法需要改变很多节点,同时还需要更新height;

 private 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;

	updateHeight(grand);
	updateHeight(parent);
	
 }
 ```cpp
 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;
	}
	
	if(child!=null){
	child.parent=grand;
	}
	grand.parent=parent;

	updateHeight(grand);
	updateHeight(parent);
	
 }

对比分析可知,rotateRight和rotate有很多共同代码,于是提取出共同部分,最后为:

 private void rotateRight(Node<E> grand) {
		 Node<E> parent =grand.left;
		 Node<E> child = parent.right;
	 
		 grand.left=child;
		 parent.right=grand;
		 
		AfterRotate(grand, parent, child);
		
	 }
	 private void rotateLeft(Node<E> grand) {
		 Node<E> parent =grand.right;
		 Node<E> child = parent.left;
	 
		 grand.right=child;
		 parent.left=grand;
		 
		 AfterRotate(grand, parent, child);
		
	 }
	 //将rotateLeft和rotateRight中的公共部分提取出来放到AfterRotate函数中
	 private void AfterRotate(Node<E> grand,Node<E> parent,Node<E> child) {
		 //让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);
	 
	 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值