数据结构查寻(2)-平衡的二叉查找树(AVL树)

数据结构查找(2)--平衡的二叉查找树(AVL树)
上篇博客介绍了二叉查找树,来看下各项操作的时间复杂度。

查找                 插入                删除
o(log n )          o(1)                  o(1)
有序              链表                 链表

我们可以想象下,在构造二叉查找树的时候,如果按以下递增序列构造如:2 4 5 6 7 8,那么构造出来的查找二叉树将会是接近链表的形式,这样就增加了查找的时间复杂度。
也就是说,这课树失去了平衡。我们看看下面这个图就明白了。
数据结构查寻(2)-平衡的二叉查找树(AVL树)
这样一来,b虽然也是一颗二叉查找树,但是查找效率大大下降。所以,我们引入了平衡的二叉查找树(AVL)树。

1.平衡
关键是“平衡”这个概念的理解。
定义如下:
树中每个结点的左、右子树深度之差的绝对值不大于1

节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。
下面给个例子,我们以左子树的高度减去它的右子树的高度作为平衡因子。
数据结构查寻(2)-平衡的二叉查找树(AVL树)

2.旋转
平衡树的关键,就是如何达到平衡,这里的关键技术就是旋转了。

下面引出的是基本概念:

有四种种情况可能导致二叉查找树不平衡,分别为:

(1)LL:插入一个新节点到根节点的左子树(Left)的左子树(Left),导致根节点的平衡因子由1变为2

(2)RR:插入一个新节点到根节点的右子树(Right)的右子树(Right),导致根节点的平衡因子由-1变为-2

(3)LR:插入一个新节点到根节点的左子树(Left)的右子树(Right),导致根节点的平衡因子由1变为2

(4)RL:插入一个新节点到根节点的右子树(Right)的左子树(Left),导致根节点的平衡因子由-1变为-2

针对四种种情况可能导致的不平衡,可以通过旋转使之变平衡。有两种基本的旋转:

(1)左旋转:将根节点旋转到(根节点的)右孩子的左孩子位置

(2)右旋转:将根节点旋转到(根节点的)左孩子的右孩子位置
给大家看张wiki上面的图,一目了然:

数据结构查寻(2)-平衡的二叉查找树(AVL树)

2.1 左左(LL)情况
针对左左这种情况,我们需要右旋。看上面的图就应该很明白。
我看人家的代码,这种情况的函数名叫做RightRotate,后来自己写着写着因为函数名把自己给搞晕了。所以我的函数是这样命名的:
左左情况,函数名就叫LeftLeft,这样就不用引入什么左旋右旋这样的概念了。免得把自己搞乱。如果大家不喜欢的话可以按照自己的理解来~~

我们附上代码:

AVLTree LeftLeft(AVLTree T)	//LL 右旋
{
	AVLTree root, pivot; 
	root = T;
	pivot = T->lchild;
	root->lchild = pivot->rchild;
	pivot->rchild = root;

	root->bf  = Max(Height(root->lchild), Height(root->rchild)) + 1;//这里的顺序不能改变,从下面到上面计算平衡因子。
	pivot->bf = Max(Height(pivot->lchild), Height(pivot->rchild)) + 1;
	return pivot;
}

2.2右右(RR)情况
针对右右这种情况,我们需要左旋(其实我们可以根本不用记左旋,只要根据上面这张wiki 的图把代码写出来就好了)
AVLTree RightRight(AVLTree T)//RR 左旋
{
	AVLTree root, pivot; 
	root = T;
	pivot = T->rchild;
	root->rchild = pivot->lchild;
	pivot->lchild = root;

	root->bf  = Max(Height(root->lchild), Height(root->rchild)) + 1;
	pivot->bf = Max(Height(pivot->lchild), Height(pivot->rchild)) + 1;
	return pivot;
}

2.3左右(LR)情况
这种情况其实是上面两种的综合,首先将pivot进行RR情况的操作(左旋),然后将root进行LL情况的操作(右旋)。
依然是根据图得出的结论,写的代码
AVLTree LeftRight(AVLTree T)//LR pivot左旋 root右旋
{
	T->lchild = RightRight(T->lchild);
	return LeftLeft(T);
}

2.4右左(RL)情况
AVLTree RightLeft(AVLTree T)//RL pivot右旋 root左旋
{
	T->rchild = LeftLeft(T->rchild);
	return RightRight(T);
}

从上面我们可以看出,RR跟LL是相互对应的,LR跟RL也是相互对应的。

把我写好的部分代码先贴出来。删除的操作还没有完成。在vs2010下测试通过.

AVLTree.h
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>

#define OK 1

typedef int TElemType ;
typedef int Status ;
typedef int KeyType ;

typedef struct AVLTNode{
	TElemType data;
	int bf;
	struct AVLTNode *lchild, *rchild, *parent;
}AVLTNode, *AVLTree;

int Height(AVLTree T);
int Max(int A, int B);
AVLTree AVLInsert(AVLTree T);

AVLTree LeftLeft(AVLTree T);		//LL型,右旋
AVLTree RightRight(AVLTree T);		//RR型,左旋
AVLTree LeftRight(AVLTree T);		//LR型,旋转
AVLTree RightLeft(AVLTree T);		//RL型,旋转

//global 
char chos;
int input;
AVLTree T = NULL;

int Height(AVLTree T)
{
	if (T == NULL)
		return -1;
	else
		return T->bf;
}


int Max(int A, int B)
{
	return ((A > B) ? A : B);
}



AVLTree AVLInsert(AVLTree T)
{
	if (NULL == T)
	{
		T = (AVLTree)malloc(sizeof(AVLTNode));
		T->data	  = input;
		T->lchild = NULL;
		T->rchild = NULL;
		T->bf	  = 0;
	}
	else if (input < T->data)
	{
		T->lchild = AVLInsert(T->lchild);
		if (Height(T->lchild) - Height(T->rchild) == 2)
		{
			if (input < T->lchild->data)
				T = LeftLeft(T);//LL
			else
				T = LeftRight(T);//LR
		}
	}

	else if (input > T->data)
	{
		T->rchild = AVLInsert(T->rchild);
		if (Height(T->rchild) - Height(T->lchild) == 2)
		{
			if (input > T->rchild->data)
				T = RightRight(T);	//RR
			else
				T = RightLeft(T);	//RL
		}
	}
	T->bf = Max(Height(T->lchild), Height(T->rchild)) + 1;
	return T;
}

AVLTree LeftLeft(AVLTree T)	//LL 右旋
{
	AVLTree root, pivot; 
	root = T;
	pivot = T->lchild;
	root->lchild = pivot->rchild;
	pivot->rchild = root;

	root->bf  = Max(Height(root->lchild), Height(root->rchild)) + 1;//这里的顺序不能改变,从下面到上面计算平衡因子。
	pivot->bf = Max(Height(pivot->lchild), Height(pivot->rchild)) + 1;
	return pivot;
}

AVLTree RightRight(AVLTree T)//RR 左旋
{
	AVLTree root, pivot; 
	root = T;
	pivot = T->rchild;
	root->rchild = pivot->lchild;
	pivot->lchild = root;

	root->bf  = Max(Height(root->lchild), Height(root->rchild)) + 1;
	pivot->bf = Max(Height(pivot->lchild), Height(pivot->rchild)) + 1;
	return pivot;
}

AVLTree LeftRight(AVLTree T)//LR pivot左旋 root右旋
{
	T->lchild = RightRight(T->lchild);
	return LeftLeft(T);
}


AVLTree RightLeft(AVLTree T)//RL pivot右旋 root左旋
{
	T->rchild = LeftLeft(T->rchild);
	return RightRight(T);
}


AVLTree.cpp
#include "AVLTree.h"

//functions
void Chose();
void Insert();
void Output(AVLTree T);
void PrintTree(AVLTree T);
void Delete();

int main()
{

	while (1)
	{
		Chose();
		switch(chos)
		{
			case 'i':
				Insert();
				break;
			case 'd':
				Delete();
				break;
			case 'e':
				exit(0);
		}
	}

	return 0;
}


void Chose()
{
	printf("i) Insert a point \n"
		   "d) Delete a point \n"
		   "e) exit			  \n"
		   "Input your choise \n");
	scanf("%c", &chos);
	getchar();
}

void Insert()
{
	printf("\nInput the point you want to Insert: ");
	scanf("%d", &input);
	getchar();
	T = AVLInsert(T);
	Output(T);
}


void Output(AVLTree T)
{
	if (T == NULL)
		printf("None\n");
	else
	{
		printf("%d\troot\n", T->data);
		PrintTree(T);
	}
}
//Tree print
void PrintTree(AVLTree T)
{
	if (T->lchild)
	{
		printf("Lchild\t%d\t[parent:%d]\n", T->lchild->data, T->data);
		PrintTree(T->lchild);			
	}
		
	if (T->rchild)
	{
		printf("Rchild\t%d\t[parent:%d]\n", T->rchild->data, T->data);
		PrintTree(T->rchild);			
	}
}

void Delete()
{

}



文献参考:

图片参考:
http://zh.wikipedia.org/wiki/AVL%E6%A0%91
基本概念参考:
http://dongxicheng.org/structure/avl/
代码参考:
http://www.asiteof.me/2010/06/avl/