平衡二叉树(AVL)

公众号:CppCoding

平衡二叉树
  • 二叉查找树给我们带来了很多方便,但是由于其在有序序列插入时就会退化成单链表(时间复杂度退化成 O(n)),AVL-tree就克服了上述困难。AVL-tree是一个“加上了平衡条件的”二叉搜索树,平衡条件确保整棵树的深度为O(log n)。

  • AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是 O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

  • 节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或-1的节点被认为是平衡的。带有平衡因子-2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

AVL旋转

在理解AVL旋转之前,首先得知道以下几个概念:

  1. AVL 树节点的插入总是在叶子节点。
  2. AVL 树在插入节点之前总是满足平衡条件的。
  3. 插入新节点后有可能满足平衡条件也有可能不满足。
  4. 当不满足平衡条件后,我们就需要对新的树进行旋转。
单旋转

在这里插入图片描述
解释:
结点18的左子节点的深度是4,右子节点的深度是1,两个的相差为2,不满足平衡条件,即18结点是不平衡点,再其是LL情况,向右旋转,即可。

双旋转

在这里插入图片描述如图,不平衡点仍是18,但是其情况变为LR,即要进行先左转,后右转

关于AVL旋转的四种情况视频

总结:
LL:不满足的点是左子节点的左子节点,要进行右转
RR:不满足的点是右子节点的右子节点,要进行左转
LR:不满足的情况是左子节点的右子节点,先进行左转,后进行右转
RL:不满足的情况是右子节点的左子节点,要先进行右转,再进行左转

AVL树的实现
结点类定义
#ifndef AVLNODE_H
#define AVLNODE_H
#include <iostream>
using namespace std;

template <class T> class AvlTree;// 声明AvlTree类
template <class T>
class AvlNode{
	T data; //关键码
	AvlNode* leftchild;
	AvlNode* rightchild; 
	int balance; //平衡因子
public:
	AvlNode():leftchild(NULL),rightchild(NULL),balance(0){}
	AvlNode(const T& e,AvlNode<T> *lt=NULL,AvlNode<T> *rt=NULL):data(e),leftchild(lt),rightchild(rt),balance(0){}
	int getBalance()const{
		return balance;
	}
	AvlNode<T>* getLeftChild()const{
		return leftchild;
	}
	AvlNode<T> * getRightChild()const{
		return rightchild;
	}
	T getData() cosnt{
		return data;
	}
	friend class AvlTree<T>;
};
#endif
AVL树类定义
template <class T>
class AvlTree{
	AvlNode<T> *root;
	bool Insert(AvlNode<T> *& rt,T x,bool &taller);
	bool Remove(AvlNode<T> *& rt,T x,bool &sharter);
	void RotateLeft(AvlNode<T> *&node); //左旋函数
	void RotateRight(AvlNode<T> *&node); //右旋函数
	void RightBalanceAfterInsert(AvlNode<T> * &sRoot,bool &taller);
	void LeftBalanceAfterInsert(AvlNode<T> * &sRoot,bool &taller);
	void RightBalanceAfterDelete(AvlNode<T> * &sRoot,bool &shorter);
	void LeftBalanceAfterDelete(AvlNode<T> * &sRoot,bool &shorter);
public:
	AvlTree():root(NULL){}
	AvlNode<T> *getRoot() const{
		return root;
	}
	bool Insert(T x){
		bool taller=false;
		return Insert(root,x,taller);
	}
	bool Remove(T x){
		bool shorter=false;
		return Remove(root,x,shorter);
	}
	//输出树形结构
	void DisplayTree(AvlNode<T> *t,int player) const;
};
AVL树类中函数的具体实现
//左旋函数代码
template <typename T>
void AvlTree<T>::RotateLeft(AvlNode<T> *& node){
	if(node == NULL) || (node->rightchild == NULL))  return ;
	AvlNode<T> * tmpNode=new AvlNode<T> (node->data); //申请新的内存存储node数据
	if(temNode == NULL) return;
	tmpNode->leftchild=node->leftchild;//考虑的是node结点下的左子节点
	node->leftchild=tmpNode;
	tmpNode->rightchild=node->rightchild->leftchild;//考虑的是node结点右子节点的左子节点
	AvlNode<T> *toDelete=node->rightchild;
	node->data=toDelete->data;
	node->rightchild=toDelete->rightchild;
	delete toDelete;
}

//右旋函数代码
template<typename T>
void AvlTree<T>::RotateRight(AvlNode<T> *& node){
	if((node==NULL)||(node->leftchild==NULL))  return;
	AvlNode<T> *tmpNode=new AvlNode<T>(node->data);
	tmpNode->rightchild=node->rightchild;
	node->rightchild=tmpNode;
	tmpNode->leftchild=node->leftchild->rightchild;
	AvlNoed<T> *toDelete=node->leftchild;
	node->data=toDelete->data;
	node->rightchild=toDelete->rightchild;
	delete toDelete;
}

//插入结点,rt的右高度增加,则调用函数进行平衡
template<typename T>
void AvlTree<T>::RightBalanceAfterInsert(AvlNode<T> *& sRoot,bool &taller){
	if((sRoot==NULL)||(sRoot->rightchild==NULL)) return;
	AvlNode<T> *rightsub=sRoot->rightchild,*leftsub;
	switch(rightsub->balance){
		case 1:
			sRoot->balance=rightsub->balance=0;
			RotateLeft(sRoot);
			taller=false;
			break;
		case 0:
			cout << "树已经平衡" << endl;
			break;
		case -1:
			leftsub=rightsub->leftchild;
			switch(leftsub->balance){
				case 1:
					sRoot->balance=-1;
					rightsub->balance=0;
					break;
				case 0:
					sRoot->balance=rightsub->balance=0;
					break;
				case -1:
					sRoot->balance=0;
					rightsub->balance=1;
					break;
			}
			leftsub->balance=0;
			RotateRight(rightsub);
			RotateLeft(sRoot);
			taller=false;
			break;
	}
}
//插入结点,rt的左高度增加,调用函数进行平衡
LeftBalanceAfterInsert函数与上面的函数类似,代码的更改只需要将上述的代码中left和right互换即可

//
template <typename T>
void AvlTree<T>::RightBalanceAfterDelete(AvlNode<T>*& sRoot,bool &shorter){
	AvlNode<T>*rightsub=sRoot->rightchild,*leftsub;
	switch(rightsub->balance){
		case 1:
			sRoot->balance=sRoot->balance=0;
			RotateLeft(sRoot);
			break;
		case 0:
			sRoot->balance=0;
			rightsub->balance=-1;
			RotateLeft(sRoot);
			break;
		case -1:
			leftsub=rightsub->leftchild;
			switch(leftsub->balance){
				case -1:
					sRoot->balance=0;
					rightsub->balance=1;
					break;
				case 0:
					sRoot->balance=rightsub->balance=0;
					break;
				case 1:
					sRoot->balance=-1;
					rightsub->balance=0;
					break;
			}
			leftsub->balance=0;
			RotateRight(rightsub);
			RotateLeft(sRoot);
			shorter=false;
			break;
	}
}


template<typename T>
bool AvlTree<T>::Insert(AvlNode<T> *& rt,T x,bool &taller){
	bool success;
	//递归函数的基本条件
	if(rt==NULL){
		rt=new AvlNode<T>(x);
		success=rt!=NULL?true:false;
		if(success) taller=true;
	}
	//如果x的值小于rt的关键码
	else if(x<rt->data){
		//insert的递归调用,从rt的左子树寻找合适的位置插入
		success=Insert(rt->leftchild,x,taller);
		if(taller){
			switch(rt->balance){
				case -1:
					LeftBalanceAfterInsert(rt,taller);
					break;
				case 0:
					rt->balance=-1;
					break;
				case 1:
					rt->balance=0;
					taller=false;
					break;
			}	
		}
	}else if(x>rt->data){
		success=Insert(rt->rightchild,x,taller);
		if(taller){
			switch(rt->balance){
				case -1:
					rt->balance=0;
					taller=false;
					break;
				case 0:
					rt->balance=1;
					break;
				case 1:
					RightBalanceAfterInsert(rt,taller);
					break;
			}
		}
	}
	return success;
}

//
template <typename T>
bool AvlTree<T>::Remove(AvlNode<T> *& rt,T x,bool &shorter){
	 bool success=false;
	 if(rt==NULL) return false;
	 //如果rt是要删除的结点
	 if(x==rt->data){
		if(rt->leftchild!=NULL&&rt->rightchild!=NULL){
			//寻找rt的中序遍历的前驱结点,用r表示
			AvlNode<T> *r=rt->leftchild;
			while(r->rightchild!=NULL){
				r=r->rightchild;
			}
			//交换rt和r的值
			T temp=rt->data;
			rt->data=r->data;
			r->data=temp;
			//递归函数,从rt的左子树中删除关键码为x的结点
			success=Remove(rt->leftchild,x,shorter);
			if(shorter){
				//如果删除后rt的左高度减少
				switch(rt->balance){
					case -1:
						rt->balance=0;
						break;
					case 0:
						rt->balance=1;
						shorter=0;
						break;
					case 1:
						RightBalanceAfterDelete(rt,shorter);
						break;		
				}
			}
		}else{
			//rt最多只有一个子女,这是递归的出口
			AvlNode<T> *p=rt;
			rt=rt->leftchild!=NULL?rt->leftchild:rt->rightchild;
			delete p;
			success=true;
			shorter=true;
		}
	}else if(x<rt->data){
		//递归调用,从rt的左子树中删除关键码为x的结点
		success=Remove(rt->leftchild,x,shorter);
		if(shorter){
			//如果删除后rt的左高渡减少
			switch(rt->balance){
				case -1:
					rt->balance=0;
					break;
				case 0:
					rt->balance=1;
					shorter=0;
					break;
				case 1:
					RightBalanceAfterDelete(rt,shorter);
					break;
			}
		}
	}else if(x>rt->data){
		//递归函数调用,从rt的右子树删除关键码为x的结点
		success=Remove(rt->rightchild,x,shorter);
		if(shorter){
			//如果删除sRoot的右高度减少
			switch(rt->balance){
				case -1:
					LeftBalanceAfterDelete(rt,shorter);
					break;
				case 0:
					rt->balance=-1;
					shorter=0;
					break;
				case 1:
					rt->balance=0;
					break;
			}
		}
	}
	return success;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值