数据结构-----AVL树的旋转操作

本文主要讲解AVL的旋转操作,供自己复习用,如有不对之处请指出。另外图片是从链接处的大神那复制的,感觉文章写的很好,可以去学习。

http://www.cnblogs.com/QG-whz/p/5167238.html具体内容可以参考链接处的讲解。

AVL树定义:一颗空的二叉树是AVL树;如果T是一棵非空的二叉树,TL和TR分别是其左子树和右子树,那么当T满足以下条件时,T是一棵AVL树:

1.TL和TR是AVL树;

2.|hl – hr| <= 1,其中hl和hr分别是TL和TR的高。

从定义可以知道,对于一个AVL树来说,左右节点的高度差只能取1, 0, -1三个值。而只有我们向树中插入一个节点或者从树中删除一个节点后,树的某些节点的高度差才会改变。也就是说,在插入和删除操作后,某些节点的左右子树的高度差可能不再是上述三个值中的任何一个。此时就需要改变这些节点的位置,来让其满足AVL树的定义。

完成一次插入或删除操作后,根据子树结构的不同,可以分成左旋,右旋,先左旋再右旋,先右旋再左旋四中方法来重构这个子树。本文以插入操作为例。

1.左旋,适用场景:插入操作完成后,插入的节点是某个节点的右节点的右节点。

《数据结构-----AVL树的旋转操作》

如图所示,假设一段子树结构中,根节点为4,右节点为5。此时如果我们插入6,根据二叉树的定义,会将6插入在5的右节点。就像上图中间那样。我们称插入的节点是根节点的右节点的右节点。

但是插入之后,对于根节点4来说,它的左节点的高度为0,右节点的高度为2,高度差不满足AVL树的定义,需要调增这些节点的位置。调整的方式就是以中间的节点为轴,向左方向旋转。即在调整之后,5是这个子结构的根节点,4是5的左节点,6是5的右节点。而5原先的左节点变成如今4的右节点(图中没有展示这个)

为了实现左旋操作,我们定义一个leftRotation函数,其参数为子结构的根节点,返回值为调整后这个子结构的根节点。

好了,接下来我们可以写出左旋函数的代码了

template<class T>
AVLTreeNode<T>* AVLTree<T>::leftRotation(AVLTreeNode<T>* pnode)
{
	AVLTreeNode<T>* prnode = pnode->rightChild;
	pnode->rightChild = prnode->leftChild;
	prnode->leftChild = pnode;

	pnode->height = max(height(pnode->leftChild), height(pnode->rightChild)) + 1;
	prnode->height = max(height(prnode->leftChild), height(prnode->rightChild)) + 1;
	return prnode;
}

代码中更新了节点高度,正如刚才提到过的,节点的位置发生改变,所以需要改变节点的高度。但是要注意先更新pnode的高度,再更新prnode的高度,因为pnode是prnode的子树。

但是为什么只需要改变节点4和节点5的高度呢?

这里说一下节点高度的概念:从该节点开始往下走,直到走到节点为NULL时停止,可以有很多条路,一条路上的节点个数为一个高度,所有的高度中最大的那个就是该节点的高度。

因为其他节点的左右子树都没有发生改变,所以高度不会发生变化。

用于左旋的插入是:插入的节点是根节点的右节点的右节点。

根节点的右节点的高度减去其左节点的高度为2,此时就需要左旋。调用的代码就像下面这样:

template<class T>
AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key)
{
	...
	if(height(pnode->rightChild) - height(pnode->leftChild) == 2)
	{
		if(pnode->rightChild->key < key)
			pnode = leftRotation(pnode)
		...
	}
	...
}

因为pnode是一个引用,比如是ppnode->leftChild的引用,调用pnode = leftRotation(pnode)后,pnode改变也就导致ppnode->leftChild的改变。

2.右旋,使用场景:插入操作完成后,插入的节点是某个节点的左节点的左节点。

《数据结构-----AVL树的旋转操作》

右旋和左旋是完全相反的两个过程。如上图,子树的根节点是4,其左节点是3,插入的2是4的左节点3的左节点。此时判断是否需要旋转只要比较4的左节点的高度减去其右节点的高度是否为2就可以了。代码和左旋类似:

template<class T>
AVLTreeNode<T>* AVLTree<T>::rightRotation(AVLTreeNode<T>* pnode)
{
	AVLTreeNode<T>* plnode = pnode->leftChild;
	pnode->leftChild = plnode->rightChild;
	plnode->rightChild = pnode;

	pnode->height = max(height(pnode->leftChild), height(pnode->rightChild)) + 1 ;
	plnode->height = max(height(plnode->leftChild), height(plnode->rightChild)) + 1;
	return plnode;
}

同样也需要更新节点的高度,但是更新高度应该先更新pnode,再更新plnode,因为此时plnode才是这个子树的根节点,它的高度需要借助其左右节点来计算,会用到pnode的高度。

使用代码的情况如左旋一样:

template<class T>
AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key)
{
	...
	if(height(pnode->leftChild) - height(pnode->rightChild) == 2)
	{
		if(pnode->leftChild->key > key)
			pnode = rightRotation(pnode)
		...
	}
	...
}

3.先左旋后右旋,使用场景:插入操作完成后,插入的节点是某个节点的左节点的右节点。

《数据结构-----AVL树的旋转操作》

这种情况,应该先对以0为根节点的子结构进行左旋操作,效果如上图第二行中间,然后再去以2为根节点的子结构进行右旋操作。

因为已经定义了左旋和右旋操作,所以左右旋和右左旋都比较好实现:

template<class T>
AVLTreeNode<T>* AVLTree<T>::leftRightRotation(AVLTreeNode<T>* pnode)
{
	pnode->leftChild = leftRotation(pnode->leftChild);
	return rightRotation(pnode);
}

调用的情况和右旋对应:

template<class T>
AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key)
{
	...
	if(height(pnode->leftChild) - height(pnode->rightChild) == 2)
	{
		if(pnode->leftChild->key > key)
			pnode = rightRotation(pnode)
		else if(pnode->leftChild->key < key)
			pnode = leftRightRotation(pnode);
		...
	}
	...
}

4.先右旋后左旋,使用场景:插入操作完成后,插入的节点是某个节点的右节点的左节点。

《数据结构-----AVL树的旋转操作》

这种情况,应该先对以8为根节点的子结构进行右旋操作,效果如第二行中间的图所示,再对以6为根节点的子结构进行左旋操作。

代码如下:

template<class T>
AVLTreeNode<T>* AVLTree<T>::rightLeftRotation(AVLTreeNode<T>* pnode)
{
	pnode->rightChild = rightRotation(pnode->rightChild);
	return leftRotation(pnode);
}

调用的方式和左旋对应:

template<class T>
AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key)
{
	...
	if(height(pnode->rightChild) - height(pnode->leftChild) == 2)
	{
		if(pnode->rightChild->key < key)
			pnode = rightRotation(pnode)
		else if(pnode->rightChild->key > key)
			pnode = rightLeftRotation(pnode);
		...
	}
	...
}

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