码迷,mamicode.com
首页 > 其他好文 > 详细

平衡二叉树

时间:2015-04-30 16:10:52      阅读:101      评论:0      收藏:0      [点我收藏+]

标签:算法   acm   二叉树   

平衡二叉树又称为AVL树,

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

维护平衡二叉树需要有四种旋转的情况

左左旋转

右右旋转

左右旋转

右右旋转

//********************************************************************************************
//***说明:二叉排序树遍历是从小到大的顺序,AVL树也是bst,但是满足所有的树的左右高度相差不大于1
//***平衡二叉树是一种特殊的二叉树,满足以上要求,主要是为了进一步的优化,提高操作的效率(O(log(n)))
//***针对下面情况进行调整
//***测试pat1066 http://www.patest.cn/contests/pat-a-practise/1066
//*********1*******************************1**********2***************************************
//**********\****************************************/*\**************************************
//***********2**************************************1***4*************************************
//************\*******------>**************************/*\************************************
//*************3**************************************3***5***********************************
//**************4*****************************************************************************
//***************\****************************************************************************
//****************5***************************************************************************
//				其中主要的策略就是选择,旋转一共分为下面四种情况
//******** A ********** B ************** C ********************* D ***************************
//********************************************************************************************
//*****    6   ****     6    ************2***********************2****************************
//*****   / \  ****    / \   ***********/*\*********************/*\***************************
//*****  3   7 ****   2   7  **********1***5*******************1***4**************************
//***** / \    ****  / \     *************/*\*********************/*\*************************
//*****1   4   **** 1   4    ************3***6*******************3***6************************
//***** \      ****    /     *************\*************************/*************************
//*****  2     ****   3      **************4***********************5**************************
//********************************************************************************************
//********************************************************************************************
//********************************************************************************************
//********************************************************************************************
//********************************************************************************************
/*********************************************************************************************
 *A、6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左。
 *B、6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右。
 *C、2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左。
 *D、2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右。
 *
 *说明:二叉排序树遍历是从小到大的顺序,AVL树也是bst,但是满足所有的树的左右高度相差不大于1
 */
#ifndef AVL_H
#define AVL_H

#include <iostream>
using namespace std;

/*AVL树节点信息*/
template <typename T>
class TreeNode
{
public :
	TreeNode():lchild(NULL),rchild(NULL),freq(1),hgt(0){}
	T data ;/*值*/
	int hgt;/*以此节点为根的树的高度*/
	unsigned int freq;/*频率*/
	TreeNode * lchild,*rchild;/*左右孩子*/
};

/*AVL树类的属性和方法声明*/
template <typename T>
class AVLTree
{
private :
	TreeNode<T> *root;								/*根节点*/

	void InsertPri(TreeNode<T> * &node, T x);		/*插入x*/
	TreeNode <T>* FindPri(TreeNode<T> *node ,T x);	/*查找x*/
	void InSubTree(TreeNode<T> *node);				/*中序遍历*/
	void DeletePri(TreeNode<T> * &node, T x);		/*删除*/
	int height(TreeNode<T> *node);					/*树的高度*/
	void SingRotateLeft(TreeNode<T> * &k2);			/*左左情况下的旋转*/
	void SingRotateRight(TreeNode<T> * &k2);		/*右右情况的旋转*/
	void DoubleRotateLR(TreeNode<T> * &k3);			/*左右情况的旋转*/
	void DoubleRotateRL(TreeNode<T> * &k3);			/*左右情况的旋转*/
	int Max(int cmpa,int cmpb);						/*求最大值*/


public :
	AVLTree():root(NULL){}
	void insert(T x);								/*插入接口*/
	TreeNode<T> * Find(T x);						/*查找接口*/
	void Delete(T x);								/*删除接口*/
	void Treversal();								/*遍历接口*/
};


/*计算以节点为根的树的高度*/
template <typename T>
int AVLTree <T>::height(TreeNode<T>* node)
{
	return node!=NULL? node->hgt:-1;
}

/*求最大值*/
template <typename T>
int AVLTree<T>::Max(int cmpa,int cmpb)
{
	return cmpa>cmpb ? cmpa :cmpb;
}

/************************************************************************/
/*	单旋转    单旋转是针对于左左和右右这两种情况的解决方案,这两种情况是
 *对称的,只要解决了左左这种情况,右右就很好办了。图3是左左情况的解决方案,
 *节点k2不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深
 *的一层的是k1的左子树X子树,所以属于左左情况    (左左情况下的选择)      */
/*为使树恢复平衡,我们把k2变成这棵树的根节点,因为k2大于k1,把k2置于k1的右
 *子树上,而原本在k1右子树的Y大于k1,小于k2,就把Y置于k2的左子树上,这样既
 *********************满足了二叉查找树的性质,又满足了平衡二叉树的性质。*/
/************************************************************************/
//****************k2******************************k1**********************
//***************/*\*****************************/*\**********************
//**************k1**z****--------->*************x***k2********************
//*************/*\*********************************/*\********************
//************x***y*******************************y***z*******************
/*这样的操作只需要一部分指针改变,结果我们得到另外一颗二叉查找树,它是一棵AVL树,因为X向
 *上一移动了一层,Y还停留在原来的层面上,Z向下移动了一层。整棵树的新高度和之前没有在左子
 *树上插入的高度相同,插入操作使得X高度长高了。因此,由于这颗子树高度没有变化,所以通往根
 *节点的路径就不需要继续旋转了。***********************************************/


/*左左旋转*/
template <typename T>
void AVLTree<T>::SingRotateLeft(TreeNode<T> * &k2)
{
	TreeNode<T> *k1;

	k1=k2->lchild;
	k2->lchild=k1->rchild;
	k1->rchild=k2;

	k2->hgt=Max(height(k2->lchild),height(k2->rchild))+1;
	k1->hgt=Max(height(k1->lchild),k2->hgt)+1;
	k2=k1;/*最后一定要更新根节点*/
}
/*右右旋转*/
template <typename T>
void AVLTree<T>::SingRotateRight(TreeNode<T> * &k2)
{
	TreeNode<T> *k1;

	k1=k2->rchild;
	k2->rchild=k1->lchild;
	k1->lchild=k2;

	k2->hgt=Max(height(k2->lchild),height(k2->rchild))+1;
	k1->hgt=Max(height(k1->rchild),k2->hgt)+1;
	k2=k1;/*最后一定要更新根节点*/
}
/**************************双旋转(左右情况下的双旋转)************************/
//************************************************************************
//*************k3******************k3**********************k2*************
//************/*\*****************/*\*********************/**\************
//**********k1***d*---->********k2***d*****---->********k1****k3**********
//*********/**\*****************/*\********************/*\***/*\**********
//********a****k2*************k1***c******************a***b*c***d*********
//************/*\************/*\******************************************
//***********b***c**********a***b*****************************************
//************************************************************************
//************************************************************************
//************************************************************************
/************************************************************************/
/*
 *左右旋转
 *为使树恢复平衡,我们需要进行两步,第一步,把k1作为根,进行一次右右旋转,旋转之后就变成
 *了左左情况,所以第二步再进行一次左左旋转,最后得到了一棵以k2为根的平衡二叉树树。
 */
template <typename T>
void AVLTree<T>::DoubleRotateLR(TreeNode<T> * &k3)
{
	SingRotateRight(k3->lchild);
	SingRotateLeft(k3);
}
/*右左旋转*/
template <typename T>
void AVLTree<T>::DoubleRotateRL(TreeNode<T> * &k3)
{
	SingRotateLeft(k3->rchild);
	SingRotateRight(k3);
}
/*************************************************************************
 *插入的方法和二叉查找树基本一样,区别是,插入完成后需要从插入的节点开始维护
 *一个到根节点的路径,每经过一个节点都要维持树的平衡。
 *维持树的平衡要根据高度差的特点选择不同的旋转算法。
 *insert
 *************************************************************************/
template <typename T>
void AVLTree<T>::InsertPri(TreeNode<T>* &node, T x)
{
	if(node==NULL)/*如果节点为空,就在此节点处加入x信息*/
	{
		node =new TreeNode<T>();
		node->data=x;
		return ;
	}
	if (node->data>x)/*如果X小于节点的值,就继续再节点的左子树中插入*/
	{
		InsertPri(node->lchild,x);
		if (2==height(node->lchild)-height(node->rchild))
		{
			if (x<node->lchild->data)
			{
				SingRotateLeft(node);
			}
			else
				DoubleRotateLR(node);
		}
	}
	else if (node->data<x)/*如果X大于节点的值,就继续再节点的右子树中插入*/
	{
		InsertPri(node->rchild,x);
		if (2==height(node->rchild)-height(node->lchild))
		{
			if (x>node->rchild->data)
			{
				SingRotateRight(node);
			}
			else
				DoubleRotateRL(node);
		}
	}
	else
		node->freq++;/*如果相等,频率加1*/
	node->hgt=Max(height(node->lchild),height(node->rchild))+1;
}

/************************************************************************/
/*                         插入接口                                      */
/************************************************************************/

template <typename T>
void AVLTree<T>::insert(T x)
{
	InsertPri(root,x);
}

/************************************************************************/
/* 查找                                                                  */
/*和二叉查找树相比,查找方法没有变法,不过根据存储的特性,
 *AVL树能维持在一个O(logN)的稳定的时间,而二叉查找树则相当不稳定。
/************************************************************************/

template<typename T>
TreeNode<T>* AVLTree<T>::FindPri(TreeNode<T> *node ,T x)
{
	if(node==NULL)/*如果节点为空说明没找到,返回NULL*/
		return NULL;
	if(node->data>x)/*如果x小于节点的值,就继续在节点的左子树中查找x*/
		return FindPri(node->lchild,x);
	if (node->data<x)/*如果x大于节点的值,就继续在节点的左子树中查找x*/
		return FindPri(node->rchild,x);
	return node;/*如果相等,就找到了此节点*/
}

/*查找接口*/
template<typename T>
TreeNode<T>* AVLTree<T>::Find(T x)
{
	return FindPri(root,x);
}

/************************************************************************/
/*   删除                                                                */
/*删除的方法也和二叉查找树的一致,区别是,删除完成后,
 *需要从删除节点的父亲开始向上维护树的平衡一直到根节点。
/************************************************************************/
template<typename T>
void AVLTree<T>::DeletePri(TreeNode<T> * &node, T x)
{
	if(node==NULL)/*空*/
		return ;
	if(x<node->data)
	{/*如果x小于节点的值,就继续在节点的左子树中删除x,删除完成后,需要调整树,使之保持平衡*/
		DeletePri(node->lchild,x);

		if (2==height(node->rchild)-height(node->lchild))
		{
			if(node->rchild->lchild!=NULL &&
				(height(node->rchild->lchild)>height(node->rchild->rchild))
				)
				DoubleRotateRL(node);
			else
				SingRotateRight(node);
		}
	}
	else if (x>node->data)
	{/*如果大于节点的值,就继续在节点的右子树中删除x,删除完成后,需要调整树,使之保持平衡*/
		DeletePri(node->rchild,x);

		if (2==height(node->lchild)-height(node->rchild))
		{
			if(node->rchild->lchild!=NULL &&
				(height(node->lchild->rchild)>height(node->lchild->lchild))
				)
				DoubleRotateLR(node);
			else
				SingRotateLeft(node);
		}
	}
	else/*相等,删除,然后调整,使之平衡*/
	{
		if (node->lchild && node->rchild)/*此节点有两个儿子*/
		{
			TreeNode<T>* temp=node->rchild;/*temp指向节点的右儿子*/
			while(temp->lchild!=NULL)
				temp=temp->lchild;/*找到中序遍历的后继节点,必定在最左边那个*/

			node->data=temp->data;/*调整节点数据信息*/
			node->freq=temp->freq;

			DeletePri(node->rchild,temp->data);/*删除边缘节点*/
			if (2==height(node->lchild)-height(node->rchild))
			{
				if (node->lchild->rchild!=NULL &&
					(height(node->lchild->rchild)>height(node->lchild->lchild))
					)
					DoubleRotateLR(node);
				else
					SingRotateLeft(node);
			}
		}
		else/*此节点有1个或0个儿子*/
		{
			TreeNode<T> *temp=node;
			if(node->lchild==NULL)/*有右儿子或者没有儿子*/
				node=node->rchild;
			else if(node->rchild==NULL)/*有左儿子*/
				node=node->lchild;
			delete(temp);
			temp=NULL;
		}
	}
	if(node==NULL)
		return ;
	node->hgt=Max(height(node->lchild),height(node->rchild))+1;
	return ;
}
/*删除接口*/
template<typename T>
void AVLTree<T>::Delete(T x)
{
	DeletePri(root,x);
}

/*中序遍历函数*/
template<typename T>
void AVLTree<T>::InSubTree(TreeNode<T> *node)
{
	if(node==NULL)
		return ;
	InSubTree(node->lchild);/*先遍历左子树*/
	cout<<node->data<<" ";/*输出根节点*/
	InSubTree(node->rchild);/*再遍历右子树*/
}
/*中序遍历接口*/
template<typename T>
void AVLTree<T>::Treversal()
{
	//cout<<root->data;
	InSubTree(root);
}
#endif//avl.h

测试用例:

#include "AVL.h"
int arrs[7]={88,70,61,96,120,90,65};
int main(int argc,char** argv)
{
	AVLTree<int> te;
	for (int i=0;i<7;i++)
	{
		te.insert(arrs[i]);
	}
	te.Treversal();
	return 0;
}


平衡二叉树

标签:算法   acm   二叉树   

原文地址:http://blog.csdn.net/u011889952/article/details/45395601

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!