AVL树的模板类实现

一、介绍

       在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是O(logn)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

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

二、AVL树的C++实现

2.1 树节点

template <class T>
struct BSTNode{
	T data;
	int h;
	BSTNode<T> * lchild, * rchild;
};

2.2 类声明

template <class T>
class AVLTree{
	public:
		AVLTree():root(NULL){}
		~AVLTree(){Release(root);}
		bool Insert(T x){return Insert(root,x);}
		bool Delete(T x){Delete(root,x);}
		bool Search(T &x){return !(Search(root,x)==NULL);}
		bool Modify(const T &x);
		void PreOrder(void visit(const T &x)){PreOrder(root,visit);}
		void InOrder(void visit(const T &x)){InOrder(root,visit);}
		
	private:
		BSTNode<T> * root;
		void Release(BSTNode<T> * bst);
		inline int Height(BSTNode<T> * bst){return (bst==NULL?-1:bst->h);}
		void SingleRotateWithLeft(BSTNode<T> * &bst);	//LL型 
		void DoubleRotateWithLeft(BSTNode<T> * &bst);	//LR型
		void SingleRotateWithRight(BSTNode<T> * &bst);	//RR型
		void DoubleRotateWithRight(BSTNode<T> * &bst);	//RL型 
		bool Insert(BSTNode<T> * &bst, const T &x);
		bool Delete(BSTNode<T> * &bst, const T &x);
		BSTNode<T> * Search(BSTNode<T> * bst, T &x);
		void PreOrder(BSTNode<T> * bst, void visit(const T& x));
		void InOrder(BSTNode<T> * bst, void visit(const T& x));
};

2.3 插入操作

        AVL树在算法设计上与普通二叉排序树的最大区别在于AVL树在插入和删除后必须进行重平衡操作。

        原理

        可以想到失衡最小的形状如下四种情况:

        

《AVL树的模板类实现》 图2.1 失衡最小形状的四种情况

          继续扩展为:

《AVL树的模板类实现》 图2.2 失衡调整(最低的失衡节点g,在g更高一侧子树内,其孩子节点p和孙子节点v必然存在)

        四种旋转函数

/*
	函数:左单旋:即LL型
	参数:bst:最小不平衡子树根指针
	返回值:空 
*/ 
template <class T>
void AVLTree<T>::SingleRotateWithLeft(BSTNode<T> * &bst)
{
	BSTNode<T> * t = bst->lchild;
	bst->lchild = t->rchild;
	t->rchild = bst;
	
	bst->h = max(Height(bst->lchild),Height(bst->rchild)) + 1;
	t->h = max(Height(t->lchild),bst->h) + 1;
	bst = t;
}
/* 
	函数:右单旋:即RR型
	参数:bst:最小不平衡子树根指针
	返回值:空 
*/ 
template <class T>
void AVLTree<T>::SingleRotateWithRight(BSTNode<T> * &bst)
{
	BSTNode<T> * t = bst->rchild;
	bst->rchild = t->lchild;
	t->lchild = bst;
	
	bst->h = max(Height(bst->lchild),Height(bst->rchild)) + 1;
	t->h = max(bst->h,Height(t->rchild)) + 1;
	bst = t;
}
/* 
	函数:左双旋:即LR型
	参数:bst:最小不平衡子树根指针
	返回值:空 
*/ 
template <class T>
void AVLTree<T>::DoubleRotateWithLeft(BSTNode<T> * &bst)
{
	SingleRotateWithRight(bst->lchild);
	SingleRotateWithLeft(bst);
}
/* 
	函数:右双旋:即RL型
	参数:bst:最小不平衡子树根指针
	返回值:空 
*/ 
template <class T>
void AVLTree<T>::DoubleRotateWithRight(BSTNode<T> * &bst)
{
	SingleRotateWithLeft(bst->rchild);
	SingleRotateWithRight(bst);
}

        插入函数可写为:

/*
	函数:插入x
	参数:bst:树的根指针,进行调整的时候会修改其值,故用引用    x:要插入的值
	返回值:是否成功插入 
*/ 
template <class T>
bool AVLTree<T>::Insert(BSTNode<T> * &bst, const T &x)
{
	if(bst == NULL){
		bst = new BSTNode<T>{x,0,NULL,NULL};
		if(bst == NULL){
			cerr << "Out of Space !!!" << endl;
			exit(1);
		}
		bst->h = max(Height(bst->lchild),Height(bst->rchild)) + 1;
		return true;
	}	
	else if(x < bst->data){
		bool r = Insert(bst->lchild, x);
		if(Height(bst->lchild) - Height(bst->rchild) == 2){
			if(x < bst->lchild->data)
				SingleRotateWithLeft(bst);
			else{
				DoubleRotateWithLeft(bst);
			}
		}
		bst->h = max(Height(bst->lchild),Height(bst->rchild)) + 1;
		return r;
	}
	else if(x > bst->data){
		bool r = Insert(bst->rchild, x);
		if(Height(bst->lchild) - Height(bst->rchild) == -2){
			if(x > bst->rchild->data)
				SingleRotateWithRight(bst);
			else{
				DoubleRotateWithRight(bst);
			}
		}
		bst->h = max(Height(bst->lchild),Height(bst->rchild)) + 1;
		return r;
	}
	else{
		cout << "插入元素已经存在!!!" << endl;
		return false;
	}
}

2.4 删除操作

        普通二叉排序树删除一个节点

         有三种情况:设待删除节点为v,其双亲节点为p

        (1)v节点为叶子节点:直接删除

        (2)v只有左子树或只有右子树:删除后让子树根节点取代删除节点

        (3)v既有左子树,又有右子树,分两种情况:

          一般情况:

            

《AVL树的模板类实现》 图2.3 

        特殊情况:

        

《AVL树的模板类实现》 图2.4

        AVL树删除一个节点

        原理:AVL树删除一个节点的操作与普通二叉排序树相同,但删除后需进行重平衡,删除操作的失衡情况与插入操作大致相同,插入操作失衡是在子树高的一端插入节点,删除操作失衡是在子树低的一端删除节点。代码实现如下:

/*
	函数:删除值为x的节点
	参数:bst:树根指针,进行调整的时候会修改其值,故用引用    x:要删除的值
	返回值:是否成功删除 
*/ 
template <class T>
bool AVLTree<T>::Delete(BSTNode<T> * &bst, const T &x)
{
	if(bst == NULL){
		cout << "删除元素不存在!!!" << endl;
		return false;
	}
	else if(x < bst->data){
		bool r = Delete(bst->lchild, x);
		if(Height(bst->lchild) - Height(bst->rchild) == -2){
			if(Height(bst->lchild) - Height(bst->rchild->rchild) == -1)
				SingleRotateWithRight(bst);
			else
				DoubleRotateWithRight(bst);
		}
		bst->h =  max(Height(bst->lchild),Height(bst->rchild)) + 1;
		return r;
	}
	else if(x > bst->data){
		bool r = Delete(bst->rchild, x);
		if(Height(bst->lchild) - Height(bst->rchild) == 2){
			if(Height(bst->lchild->lchild) - Height(bst->rchild) == 1)
				SingleRotateWithLeft(bst);
			else
				DoubleRotateWithLeft(bst);
		}
		bst->h =  max(Height(bst->lchild),Height(bst->rchild)) + 1;
		return r;
	}
	else{
		if(bst->lchild == NULL && bst->rchild == NULL){
			delete bst;
			bst = NULL;
		}
		else if(bst->lchild != NULL && bst->rchild == NULL){
			BSTNode<T> * p = bst;
			bst = bst->lchild;
			delete p;
		}
		else if(bst->lchild == NULL && bst->rchild != NULL){
			BSTNode<T> * p = bst;
			bst = bst->rchild;
			delete p;
		}
		else{
			BSTNode<T> * p = bst->rchild;
			while(p->lchild != NULL){
				p = p->lchild;
			}
			bst->data = p->data;
			if(p == bst->rchild)//特殊情况 
				bst->rchild = p->rchild;
			else{				//一般情况 
				bst->rchild->lchild = p->rchild;
				bst->rchild->h = max(Height(bst->rchild->lchild),Height(bst->rchild->rchild))+1;
			}
			delete p;
		}
		if(bst != NULL)
			bst->h =  max(Height(bst->lchild),Height(bst->rchild)) + 1;
		return true;
	}
}

2.5 其他

其他未给出的代码已经放到github上(点击访问),可以访问下载。

三、拓展

统一重平衡算法:

上述重平衡的方法,需要根据失衡节点及其孩子节点、孙子节点的相对位置关系,分别做单旋或双旋调整。按照这一思路直接实现调整算法,代码量大且流程繁杂,必然导致调试困难且容易出错。为此引入一种更为简明的统一处理方法。

无论对于插入或删除操作,新方法也同样需要从刚发生修改的位置x出发逆行而上,直至遇到最低的失衡节点g(x)。于是在g(x)更高一侧的子树内,其孩子节点p和孙子节点v必然存在,而且这一局部必然可以g(x)、p和v为界,分解为四棵子树,将它们按中序遍历次序重命名为《AVL树的模板类实现》《AVL树的模板类实现》

若同样按照中序遍历次序,重新排列g(x)、p和v,并将其命名为a、b和c,则这一局部的中序遍历序列应为:

{《AVL树的模板类实现》,a,《AVL树的模板类实现》,b,《AVL树的模板类实现》,c,《AVL树的模板类实现》}

这就意味着,这一局部应等价于如图3.1所示的子树。更重要的是,纵观图2.2的四棵子树可见,它们的高度彼此相差不超过一层,故只需如图3.1所示,将这三个节点与四棵子树重新“组装”起来,恰好即是一颗AVL树!

《AVL树的模板类实现》 图3.1 统一重新平衡

实际上,这一理解涵盖了此前两节所有的单旋和双旋情况。相应的重构过程,仅涉及局部的三个节点及其四棵子树,故称作“3+4”重构。 

四、总结

本文算法的实现主要参考了《数据结构与算法分析:C语言描述》、《数据结构 C++语言版 邓俊辉》、《数据结构 C++版 王红梅》,对于有理解不正确的地方或代码有bug,欢迎在留言区评论。

 

箴言录:

        勿以恶小而为之,勿以善小而不为。

    原文作者:AVL树
    原文地址: https://blog.csdn.net/u014296991/article/details/80671750
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞