数据结构与算法分析 c++ 平衡二叉树 AvlTree

    一棵AVL 树(AVL tree) 是其每个节点的左子树和又子树的高度最多差1的二叉查找树。 可以通过单旋转和双旋转来达到平衡条件。 

这个实现起来有一定难度,参考书上和网上的程序实现, 详见P118–127。

AvlNode.h

#pragma once

#include <iostream>
#include <string>

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

#define EQ(a,b) ((a) == (b))  
#define LT(a,b) ((a) < (b))  
#define LQ(a,b) ((a) <= (b))  

//结点元素类型   
typedef struct Student
{
	int key;
	std::string major;
	Student() {}
	Student(int k, std::string s) : key(k), major(s) {}
}ElementType;


extern std::ostream& operator << (std::ostream& out, const Student& s);
extern std::istream& operator >> (std::istream& in, Student& s);

typedef int KeyType;//关键字类型   

typedef struct AVLNode
{
	ElementType data;
	int bf;
	struct AVLNode* lchild;
	struct AVLNode* rchild;

	AVLNode() {}
	AVLNode(ElementType& e, int ibf = EH, AVLNode* lc = NULL, AVLNode* rc = NULL)
		: data(e), bf(ibf), lchild(lc), rchild(rc) {}
}AVLNode, *AVL;


void initAVL(AVL& t);
void printAVL(AVL t);
void inOrderTraverse(AVL t);
AVLNode* searchAVL(AVL& t, KeyType key);
bool insertAVL(AVL& t, ElementType& e, bool& taller);
bool deleteAVL(AVL& t, KeyType key, bool& shorter);
void destroyAVL(AVL& t);

AvlNode.cpp

#include "AvlNode.h"

using namespace std;

std::ostream& operator<<(std::ostream& out, const Student& s)
{
	out << "(" << s.key << "," << s.major << ")";
	return out;
}

std::istream& operator >> (std::istream& in, Student& s)
{
	in >> s.key >> s.major;
	return in;
}

/*
*Description: 初始化(其实可以不用)
*/
void initAVL(AVL& t)
{
	t = NULL;
}

/*
*Description: 销毁平衡二叉树
*/
void destroyAVL(AVL& t)
{
	if (t)
	{
		destroyAVL(t->lchild);
		destroyAVL(t->rchild);
		delete t;
		t = NULL;
	}
}

/****************************************************************************/
/*                           遍历和查找                                     */
/****************************************************************************/

//前序遍历  
void preOrderTraverse(AVL t)
{
	if (t)
	{
		cout << t->data << " ";
		preOrderTraverse(t->lchild);
		preOrderTraverse(t->rchild);
	}
}

//中序遍历  
void inOrderTraverse(AVL t)
{
	if (t)
	{
		inOrderTraverse(t->lchild);
		cout << t->data << " ";
		inOrderTraverse(t->rchild);
	}
}

//以前序和中序输出平衡二叉树  
void printAVL(AVL t)
{
	cout << "inOrder: " << endl;
	inOrderTraverse(t);
	cout << endl;
	cout << "preOrder: " << endl;
	preOrderTraverse(t);
	cout << endl;
}

/*
Description:
在根指针t所指平衡二叉树中递归地查找某关键字等于key的数据元素,
若查找成功,则返回指向该数据元素结点的指针,否则返回空指针。
根据需要,也可以返回一个bool值
*/
AVLNode* searchAVL(AVL& t, KeyType key)
{
	if ((t == NULL) || EQ(key, t->data.key))
		return t;
	else if LT(key, t->data.key) /* 在左子树中继续查找 */
		return searchAVL(t->lchild, key);
	else
		return searchAVL(t->rchild, key); /* 在右子树中继续查找 */
}

/****************************************************************************/
/*                            旋转处理                                      */
/****************************************************************************/
/*
Description:
对以*p为根的二叉排序树作左旋处理,处理之后p指向新的树根结点,即旋转
处理之前的右子树的根结点。也就是书上说说的RR型.
*/
void L_Rotate(AVLNode* &p)
{
	AVLNode * rc = NULL;
	rc = p->rchild;          //rc指向p的右子树根结点  
	p->rchild = rc->lchild;//rc的左子树挂接为p的右子树   
	rc->lchild = p;
	p = rc;                 //p指向新的根结点   
}

/*
Description:
对以*p为根的二叉排序树作右旋处理,处理之后p指向新的树根结点,即旋转
处理之前的左子树的根结点。也就是书上说说的LL型.
*/
void R_Rotate(AVLNode* &p)
{
	AVLNode * lc = NULL;
	lc = p->lchild;     //lc指向p的左子树根结点  
	p->lchild = lc->rchild;   //lc的右子树挂接为p的左子树   
	lc->rchild = p;
	p = lc;                 //p指向新的根结点   
}

/****************************************************************************/
/*                          平衡处理                                        */
/****************************************************************************/

/*
左平衡处理
对以指针t所指结点为根的二叉树作左平衡旋转处理
包含LL旋转和LR旋转两种情况
平衡因子的改变其实很简单,自己画图就出来了
*/
void leftBalance(AVLNode* &t)
{
	AVLNode* lc = NULL;
	AVLNode* rd = NULL;
	lc = t->lchild;
	switch (lc->bf)
	{
	case LH:                    //LL旋转   
		t->bf = EH;
		lc->bf = EH;
		R_Rotate(t);
		break;

	case EH:                    //deleteAVL需要,insertAVL用不着   
		t->bf = LH;
		lc->bf = RH;
		R_Rotate(t);
		break;

	case RH:                    //LR旋转   
		rd = lc->rchild;
		switch (rd->bf)
		{
		case LH:
			t->bf = RH;
			lc->bf = EH;
			break;
		case EH:
			t->bf = EH;
			lc->bf = EH;
			break;
		case RH:
			t->bf = EH;
			lc->bf = LH;
			break;
		}
		rd->bf = EH;
		L_Rotate(t->lchild);//不能写L_Rotate(lc);采用的是引用参数   
		R_Rotate(t);
		break;
	}
}

/*
右平衡处理
对以指针t所指结点为根的二叉树作右平衡旋转处理
包含RR旋转和RL旋转两种情况
*/
void rightBalance(AVLNode* &t)
{
	AVLNode* rc = NULL;
	AVLNode *ld = NULL;

	rc = t->rchild;
	switch (rc->bf)
	{
	case LH:                //RL旋转   
		ld = rc->lchild;
		switch (ld->bf)
		{
		case LH:
			t->bf = EH;
			rc->bf = RH;
			break;
		case EH:
			t->bf = EH;
			rc->bf = EH;
			break;
		case RH:
			t->bf = LH;
			rc->bf = EH;
			break;
		}
		ld->bf = EH;
		R_Rotate(t->rchild);//不能写R_Rotate(rc);采用的是引用参数   
		L_Rotate(t);
		break;

	case EH:                //deleteAVL需要,insertAVL用不着   
		t->bf = RH;
		rc->bf = LH;
		L_Rotate(t);
		break;

	case RH:                //RR旋转   
		t->bf = EH;
		rc->bf = EH;
		L_Rotate(t);
		break;
	}
}

/****************************************************************************/
/*                          插入删除处理                                    */
/****************************************************************************/

/*
 插入处理
若在平衡的二叉排序树t中不存在和e有相同关键字的结点,则插入一个
数据元素为e的新结点,并返回true,否则返回false。若因插入而使二叉排序树
失去平衡,则作平衡旋转处理,布尔变量taller反映t长高与否
*/
bool insertAVL(AVL& t, ElementType& e, bool& taller)
{
	if (t == NULL)
	{
		t = new AVLNode(e);             //插入元素   
		taller = true;
	}
	else
	{
		if (EQ(e.key, t->data.key))       //树中已含该关键字,不插入   
		{
			taller = false;
			return false;
		}
		else if (LT(e.key, t->data.key))//在左子树中查找插入点   
		{
			if (!insertAVL(t->lchild, e, taller))//左子树插入失败   
			{
				return false;
			}
			if (taller)                  //左子树插入成功,且左子树增高   
			{
				switch (t->bf)
				{
				case LH:            //原来t的左子树高于右子树   
					leftBalance(t); //做左平衡处理   
					taller = false;
					break;
				case EH:            //原来t的左子树和右子树等高   
					t->bf = LH;      //现在左子树比右子树高   
					taller = true;  //整棵树增高了   
					break;
				case RH:            //原来t的右子树高于左子树  
					t->bf = EH;      //现在左右子树等高   
					taller = false;
					break;
				}
			}
		}
		else                            //在右子树中查找插入点   
		{
			if (!insertAVL(t->rchild, e, taller))//右子树插入失败   
			{
				return false;
			}
			if (taller)                  //右子树插入成功,且右子树增高  
			{
				switch (t->bf)
				{
				case LH:            //原来t的左子树高于右子树   
					t->bf = EH;
					taller = false;
					break;
				case EH:            //原来t的左子树和右子树等高   
					t->bf = RH;
					taller = true;
					break;
				case RH:            //原来t的右子树高于左子树  
					rightBalance(t);//做右平衡处理  
					taller = false;
					break;
				}
			}
		}
	}
	return true;                        //插入成功   
}

/*
删除处理
若在平衡的二叉排序树t中存在和e有相同关键字的结点,则删除之
并返回true,否则返回false。若因删除而使二叉排序树
失去平衡,则作平衡旋转处理,布尔变量shorter反映t变矮与否
*/
bool deleteAVL(AVL& t, KeyType key, bool& shorter)
{
	if (t == NULL)                       //不存在该元素   
	{
		return false;                   //删除失败   
	}
	else if (EQ(key, t->data.key))        //找到元素结点  
	{
		AVLNode* q = NULL;
		if (t->lchild == NULL)            //左子树为空   
		{
			q = t;
			t = t->rchild;
			delete q;
			shorter = true;
		}
		else if (t->rchild == NULL)       //右子树为空   
		{
			q = t;
			t = t->lchild;
			delete q;
			shorter = true;
		}
		else                            //左右子树都存在,  
		{
			q = t->lchild;
			while (q->rchild)
			{
				q = q->rchild;
			}
			t->data = q->data;
			deleteAVL(t->lchild, q->data.key, shorter);   //在左子树中递归删除前驱结点   
		}
	}
	else if (LT(key, t->data.key))        //左子树中继续查找   
	{
		if (!deleteAVL(t->lchild, key, shorter))
		{
			return false;
		}
		if (shorter)
		{
			switch (t->bf)
			{
			case LH:
				t->bf = EH;
				shorter = true;
				break;
			case EH:
				t->bf = RH;
				shorter = false;
				break;
			case RH:
				rightBalance(t);    //右平衡处理  
				if (t->rchild->bf == EH)//注意这里,画图思考一下   
					shorter = false;
				else
					shorter = true;
				break;
			}
		}
	}
	else                                //右子树中继续查找   
	{
		if (!deleteAVL(t->rchild, key, shorter))
		{
			return false;
		}
		if (shorter)
		{
			switch (t->bf)
			{
			case LH:
				leftBalance(t);     //左平衡处理   
				if (t->lchild->bf == EH)//注意这里,画图思考一下   
					shorter = false;
				else
					shorter = true;
				break;
			case EH:
				t->bf = LH;
				shorter = false;
				break;
			case RH:
				t->bf = EH;
				shorter = true;
				break;
			}
		}
	}
	return true;
}

main.cpp    // 测试AVL tree

#include <iostream>
#include <cstdlib>   
#include <string>  
#include "AvlNode.h"

using namespace std;



int main(int argc, char *argv[])
{
	AVL t;
	initAVL(t);
	bool taller = false;
	bool shorter = false;
	int key;
	string major;
	ElementType e;
	int choice = -1;
	bool flag = true;

	while (flag)
	{
		cout << "--------------------" << endl;
		cout << "0. print" << endl
			<< "1. insert" << endl
			<< "2. delete" << endl
			<< "3. search" << endl
			<< "4. exit" << endl
			<< "--------------------" << endl
			<< "please input your choice: ";
		cin >> choice;
		switch (choice)
		{
		case 0:
			printAVL(t);
			cout << endl << endl;
			break;
		case 1:
			inOrderTraverse(t);
			cout << endl << "input the elements to be inserted,end by 0:" << endl;
			while (cin >> key && key)
			{
				cin >> major;
				ElementType e(key, major);
				if (insertAVL(t, e, taller))

				{
					cout << "insert element " << e << " successfully" << endl;
				}
				else
				{
					cout << "there already exists an element with key " << e.key << endl;
				}
			}
			//while(cin>>e && e.key)  
			//              {  
			//                  if(insertAVL(t,e,taller))  
			//                  {  
			//                      cout<<"insert element "<<e<<" successfully"<<endl;  
			//                  }  
			//                  else  
			//                  {  
			//                      cout<<"there already exists an element with key "<< e.key<<endl;  
			//                  }  
			//              }  
			cout << "after insert: " << endl;
			printAVL(t);
			cout << endl << endl;
			break;

		case 2:
			inOrderTraverse(t);
			cout << endl << "input the keys to be deleted,end by 0:" << endl;
			while (cin >> key && key)
			{
				if (deleteAVL(t, key, shorter))
				{
					cout << "delete the element with key " << key << " successfully" << endl;
				}
				else
				{
					cout << "no such an element with key " << key << endl;
				}
			}
			cout << "after delete: " << endl;
			printAVL(t);
			cout << endl << endl;
			break;

		case 3:
			inOrderTraverse(t);
			cout << endl << "input the keys to be searched,end by 0:" << endl;
			while (cin >> key && key)
			{
				if (searchAVL(t, key))
					cout << key << " is in the tree" << endl;
				else
					cout << key << " is not in the tree" << endl;
			}
			cout << endl << endl;
			break;

		case 4:
			flag = false;
			break;

		default:
			cout << "error! watch and input again!" << endl << endl;

		}
	}
	destroyAVL(t);

	return 0;
}

2、测试用例

(1)  输入1,开始insert。接着输入要插入的数据元素,每行一个(学号和专业之间以空格分隔),如果采用的是重载>>后的输入方式,那么以 0 0作为结束,如果采用的是另外的方式,直接输入0结束,上面的代码插入删除查找都是以0作为输入结束。

20 music

10 english

5 physics

30 chinese

40 language

15 japanese

25 biology

23 mathematics

50 chemistry

1 physics

3 geography

0

   插入完成后,会给出提示,最后给出前序和中序输出。可以对比下面的图看是否正确。

(2)  输入3,进行search。依次输入1 2 3 5 7 8 10 13 15 17 20 23 30 31 50 60 0

观察输出结果看是否正确

(3)  输入2,进行delete。依次输入15 23 25 1 30 50 40 3 0

(4)  输入1,打印平衡二叉树。比较看看输出和自己画的是否相符。

参考:http://blog.csdn.net/sysu_arui/article/details/7906303

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