avl.cpp

来自「平衡二叉树是数据结构中一个非常重要的概念。它对二叉树的优化和提高查询效率有重要的」· C++ 代码 · 共 566 行

CPP
566
字号
#include <stdlib.h>
#include <iostream.h>	//cout
#include <iomanip.h>	//setw

# define LH 1		//左高
# define EH 0		//等高
# define RH -1		//右高

# define TRUE 1
# define FALSE 0

int taller=0;		//taller反映T长高与否
int shorter=0;		//shorter反映T变矮与否

//二叉排序树的类型定义
typedef struct BSTNode
{   
	int data;		//结点值
	int bf;			//结点的平衡因子
	struct BSTNode * lchild, * rchild;	//分别指向左右孩子的指针
}BSTNode, *BSTree;	//同时声明一个BSTNode和一个指针类型的*BSTree


//树的中序遍历的递归算法,一并输出它的平衡因子和左右结点值
void MidOrder(BSTree T)
{   
	//中序遍历的特点是:
	//当二叉树为空,则空操作;否则
	//1.中序遍历左子树;
	//2.访问根结点;
	//3.中序遍历右子树。
	if(T->lchild) MidOrder(T->lchild);   
	if(T->data) 
	{
		cout<<setw(4)<<T->data<<setw(6)<<T->bf;
		if (T->lchild ) cout<<setw(8)<<T->lchild->data; else cout<<setw(8)<<"--";
		if (T->rchild ) cout<<setw(8)<<T->rchild->data; else cout<<setw(8)<<"--";
		cout<<endl;
	}	
	if(T->rchild) MidOrder(T->rchild); 
}


//树的先序遍历的递归算法,一并输出它的平衡因子和左右结点值
void RootOrder(BSTree T)
{   
	//先序遍历的特点是:
	//当二叉树为空,则空操作;否则
	//1.访问根结点;
	//2.先序遍历左子树;
	//3.先序遍历右子树。
	if(T->data) 
	{
		cout<<setw(4)<<T->data<<setw(6)<<T->bf;
		if (T->lchild ) cout<<setw(8)<<T->lchild->data; else cout<<setw(8)<<"--";
		if (T->rchild ) cout<<setw(8)<<T->rchild->data; else cout<<setw(8)<<"--";
		cout<<endl;
	}
	if(T->lchild) RootOrder(T->lchild);   	
	if(T->rchild) RootOrder(T->rchild); 
}


//对以p为根的二叉排序树作右旋处理,处理之p指向新的树根结点即旋转处理之前的左子树根结点
BSTree R_Rotate(BSTree p)
{	
	BSTNode *lc;			//声明BSTNode* 临时变量
	lc=p->lchild;			//lc指向的*p的左子树根结点
	p->lchild=lc->rchild;	//lc的右子树挂接为*p的左子树
	lc->rchild=p;			
	p=lc;					//p指向新的根结点
	return p;				//返回新的根结点
}

//对以p为根的二叉排序树作左旋处理,处理之p指向新的树根结点即旋转处理之前的右子树根结点
BSTree L_Rotate(BSTree p)
{   
	BSTNode *rc;			//声明BSTNode* 临时变量
	rc=p->rchild;			//rc指向的*p的右子树根结点
	p->rchild=rc->lchild;	//rc的左子树挂接为*p的右子树
	rc->lchild=p;
	p=rc;					//p指向新的根结点
	return p;				//返回新的根结点
}

//对以指针T所指结点为根的二叉树作左平衡旋转处理,本算法结束时指针T指向新的根结点
BSTree LeftBalance(BSTree T)
{   
	BSTNode *lc,*rd;
	lc=T->lchild;				//lc指向*T的左子树根结点
	switch(lc->bf)				//检查*T的左子树平衡度,并做相应的平衡处理
	{
		case LH:				//新结点插入在*T的左孩子的左子树上,要做单右旋处理
			T->bf=lc->bf=EH;
			T=R_Rotate(T);
			break;
		case RH:				//新结点插入在*T的左孩子的右子树上,要做双旋处理
			rd=lc->rchild;		//rd指向*T的左孩子的右子树根
			switch(rd->bf)		//修改*T及其左孩子的平衡因子
			{
				case LH:
					T->bf=RH;
					lc->bf=EH;
					break;
				case EH:
					T->bf=lc->bf=EH;
					break;
				case RH:
					T->bf=EH;
					lc->bf=LH;
					break;
			}
			rd->bf=EH;
			T->lchild=L_Rotate(T->lchild);	//对*T的左孩子做左旋平衡处理
			T=R_Rotate(T);					//对*T做右旋处理
	}
	return T;
}

//对以指针T所指结点为根的二叉树作右平衡旋转处理,本算法结束时指针T指向新的根结点
BSTree RightBalance(BSTree T)
{   
	BSTree rc,ld;
	rc=T->rchild;				//rc指向*T的右子树根结点
	switch(rc->bf)				//检查*T的右子树平衡度,并做相应的平衡处理
	{
		case RH:				//新结点插入在*T的右孩子的右子树上,要做单右旋处理
			T->bf=rc->bf=EH;
			T=L_Rotate(T);
			break;
		case LH:				//新结点插入在*T的右孩子的左子树上,要做双旋处理
			ld=rc->lchild;		//ld指向*T的右孩子的左子树根
			switch(ld->bf)		//修改*T及其右孩子的平衡因子
			{
				case LH:
					T->bf=LH;
					rc->bf=EH;
					break;
				case EH:
					T->bf=rc->bf=EH;
					break;
				case RH:
					T->bf=EH;
					rc->bf=RH;
					break;
			}
			ld->bf=EH;
			T->rchild=R_Rotate(T->rchild);	//对*T的右孩子做右旋平衡处理
			T=L_Rotate(T);					//对*T做左旋处理
	}
	return T;
}



//若在平衡的二叉排序树T中不存在和e有相同关键字的结点,则插入一个
//数据元素为e的新结点,并返回插入后所建成的平衡二叉排序树,否则返回
//NULL.若因插入而使二叉数失去平衡,则作平衡旋转处理,布尔变量taller
//反映T长高与否
BSTree InsertAVL(BSTree T, int e)
{   
	BSTree p;

	//插入新结点,树长高置taller为TRUE
	if(!T)
	{
		T=(BSTree)malloc(sizeof(BSTNode));
		T->data=e;
		T->lchild=T->rchild=NULL;
		T->bf=EH;
		taller=TRUE;
	}
	else
	{
		//树中存在和e有相同关键字的结点则不再插入
		if(e==T->data)
		{
			taller=FALSE;
			return NULL;
		}

		//值小于则继续在树的左子树中搜索
		if(e < T->data)
		{
			//插入到左子树且左子树长高
			p=InsertAVL(T->lchild,e);
			if(p)
			{
				T->lchild=p;
				if(taller)
				{
					switch(T->bf)				//检查*T的平衡度
					{
						case LH:				//原本左子树比右子树高,需要做左平衡处理
							T=LeftBalance(T);
							taller=FALSE;
							break;
						case EH:				//原本左子树和右子树同高,现因左子树争高而使树增高
							T->bf=LH;
							taller=TRUE;
							break;
						case RH:				//原本右子树比左子树高,现在左右子树等高
							T->bf=EH;
							taller=FALSE;
							break;
					}
				}
			}
		}
		//继续在*T的右子树中搜索
		else
		{
			//插入到右子树且使右子树长高
			p=InsertAVL(T->rchild,e);
			if (p)
			{
				T->rchild=p;
				if(taller)
				{
					switch(T->bf)				//检查*T的平衡度
					{
						case LH:				//原本左子树比右子树高,现在左右子树等高
							T->bf=EH;
							taller=FALSE;
							break;
						case EH:				//原本左子树和右子树同高,现因右子树增高而使树增高
							T->bf=RH;
							taller=TRUE;
							break;
						case RH:				//原本右子树比左子树高,需要做右平衡处理
							T=RightBalance(T);	
							taller=FALSE;
							break;
					}
				}
			}
		}
	}
	return T;
}

//删除结点时对以指针T所指结点为根的二叉树作左平衡旋转处理,本算法结束时指针T指向新的根结点
BSTree LeftBalance1(BSTree p)
{   
	BSTree  p1,p2;
	//左子树比右子树高
	if(p->bf==1)
	{
		p->bf=0;
		shorter=1;
	}
	else 
		if(p->bf==0)
		{
			p->bf=-1;
			shorter=0;
		}
		else
		{
			p1=p->rchild;
			if(p1->bf==0)
			{
				p->rchild = p1->lchild;
				p1->lchild = p;
				p1->bf = 1;
				p->bf = -1;
				p = p1;
				shorter = 0;
			}
			else 
				if(p1->bf==-1)
				{
					p->rchild=p1->lchild;
					p1->lchild=p;
					p1->bf=p->bf=0;
					p=p1;
					shorter=1;
				}
				else
				{
					p2=p1->lchild;
					p1->lchild=p2->rchild;
					p2->rchild=p1;
					p->rchild=p2->lchild;
					p2->lchild=p;

					if(p2->bf==0)
					{
						p->bf=0;
						p1->bf=0;
					}
					else 
						if(p2->bf==-1)
						{
							p->bf=1;
							p1->bf=0;
						}
						else
						{
							p->bf=0;
							p1->bf=-1;
						}

					p2->bf=0;
					p=p2;
					shorter=1;
				}
		}

	return p;
}

//删除结点时对以指针T所指结点为根的二叉树作右平衡旋转处理,本算法结束时指针T指向新的根结点
BSTree RightBalance1(BSTree p)
{   
	BSTree  p1,p2;
	if(p->bf==-1)
	{
		p->bf=0;
		shorter=1;
	}
	else 
		if(p->bf==0)
		{
			p->bf=1;
			shorter=0;
		}
		else
		{
			p1=p->lchild;
			if(p1->bf==0)
			{
				p->lchild = p1->rchild;
				p1->rchild = p;
				p1->bf=-1;
				p->bf=1;
				p=p1;
				shorter=0;
			}
			else 
				if(p1->bf==1)
				{
					p->lchild=p1->rchild;
					p1->rchild=p;
					p1->bf=p->bf=0;
					p=p1;
					shorter=1;
				}
				else
				{
					p2=p1->rchild;
					p1->rchild = p2->lchild;
					p2->lchild = p1;
					p->lchild = p2->rchild;
					p2->rchild = p;
					if(p2->bf == 0)
					{
						p->bf = 0;
						p1->bf = 0;
					}
					else 
						if(p2->bf == 1)
						{
							p->bf = -1;
							p1->bf = 0;
						}
						else
						{
							p->bf=0;
							p1->bf=1;
						}

					p2->bf=0;
					p=p2;
					shorter=1;
				}
		}

	return p;
}

//对结点进行删除处理
BSTree Delete(BSTree q, BSTree r)
{   
	if(r->rchild==NULL)	//根结点的右子树为空则将左子树提高并标记树变矮了
	{
		q->data=r->data;
		q=r;
		r=r->lchild;
		free(q);
		shorter=1;
	}
	else				//继续递规搜索并删除
	{
		r->rchild=Delete(q,r->rchild);
	} 
	return r;
}

//找到要删除的结点,并调用删除函数对其进行删除
BSTree DeleteAVL(BSTree p, int e)
{   
	BSTree q;
	//抛弃空删除
	if(p==NULL) return NULL;
	else 
		if(e < p->data)			//欲删除值小于当前结点值则继续在左子树中搜索
		{
			p->lchild = DeleteAVL(p->lchild,e);
			if(shorter==1) p=LeftBalance1(p);
			return p;
		}
		else 
			if(e > p->data)		//欲删除值大于当前结点值则继续在右子树中搜索
			{
				p->rchild=DeleteAVL(p->rchild,e);
				if(shorter==1) p=RightBalance1(p);
				return p;
			}
			else				//找到了
			{
				q=p;			//将p存储到临时变量q里面
				if(p->rchild==NULL)		//如果p的右子树为空则将其左子树提高一层
				{
					p=p->lchild;
					free(q);
					shorter=1;			//并标记树变矮了
				}
				else				//如果p的左子树为空则将其右子树提高一层
					if(p->lchild==NULL)
					{
						p=p->rchild;
						free(q);
						shorter=1;
					}
					else
					{
						//将删除后的子树后的得到的新的子树的根结点赋值给q左子树
						q->lchild=Delete(q,q->lchild);
						//如果树高到变矮了则要执行删除后的左平衡处理
						if(shorter==1) q=LeftBalance1(q);
						p=q;
					}
			}

	return p;
}

//建立新结点
BSTree CreatNode(int nodeValue)
{
	BSTree node; 
    node = (BSTree)malloc(sizeof(BSTree));
    node->data = nodeValue;
    node->bf = 0;
    node->lchild = NULL;
    node->rchild = NULL;
    return node;
}

//在屏幕打印菜单函数
int PrintMenu()
{
    int userChose;
    cout << "---------------------" << endl;
    cout << "1. 创建一棵二叉平衡树" << endl;
    cout << "2. 向平衡树插入新结点" << endl;
	cout << "3. 从树中删除一个结点" << endl;
    cout << "4. 中序遍历输出平衡树" << endl;
    cout << "5. 先序遍历输出平衡树" << endl;
	cout << "n. 选择其它将退出程序" << endl;
    cout << "---------------------" << endl;
    cout << "请选择你的操作:";
    cin >> userChose;
    return userChose;
}

//新建二叉树函数
BSTree BuildTree(BSTree r)
{
    //如果传入根结点不为空,则树已构建过,退出函数 
    if (r != NULL)
    {
        cout << "二叉平衡树已经创建!";
        return NULL;
    }
    
    //根结点为空则开始构建
    cout << "请输入结点值,输入零则结束" << endl;

    int nodeValue;
    cin >> nodeValue;

    while  (nodeValue)	//插入任何不为0的整数值
    {
        //建立新结点
        BSTree node = CreatNode(nodeValue);
        //如果根为空,则将此结点设置为根结点 
        if (r == NULL)
        {
            r = node;
        }
        //否则将此结点作为非根结点插入 
        else
        {
            r=InsertAVL(r,nodeValue);
        }
        //输入新值
        cin >> nodeValue;
    }
    return r;
    
}

void main()
{
    //定义一个空根结点
	BSTree root=NULL;
	int e;

    //在屏幕打印菜单,让用户选择建立、遍历、插入、删除等功能

    int userChoseMenu;
    while (userChoseMenu = PrintMenu())
    {
        //根据用户不同选择作出不同处理
        switch (userChoseMenu)
        {
            case 1 : 
                cout << "你选择了1,请依次输入新树的值,回车输入下一个,遇0结束输入。" << endl;
                root = BuildTree(root);
                break;
            case 2 :
                cout << "你选择了2,请输入要插入的结点值!" << endl;
				cin >> e;
				root = InsertAVL(root,e);
                break;
            case 3 :
                cout << "你选择了3,请输入要删除的结点值!" << endl;
				cin >> e;
				root = DeleteAVL(root,e);
                break;
            case 4 :
                cout << "你选择了4,中序遍历输出二叉平衡树。" << endl;
				cout<<"----------------------------"<<endl;
				cout<<" 数据 "<<"平衡因子 "<<"左子树 "<<"右子树"<<endl;
                MidOrder(root);
				cout<<"----------------------------"<<endl;
                break;
            case 5 :
                cout << "你选择了5,先序遍历输出二叉平衡树。" << endl;
				cout<<"----------------------------"<<endl;
				cout<<" 数据 "<<"平衡因子 "<<"左子树 "<<"右子树"<<endl;
				RootOrder(root);
				cout<<"----------------------------"<<endl;
                break;
            default :
                cout << "无效选择,程序将退出。" << endl;
				exit(0);
                break;
        }
    }

	//程序存在已知错误,当删除以下结点是会出现异常,忽略异常后输出的结果错误
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?