您的位置:首页 > 理论基础 > 数据结构算法

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

2012-12-16 15:39 369 查看
上篇博客介绍了二叉查找树,来看下各项操作的时间复杂度。

查找 插入 删除

o(log n ) o(1) o(1)

有序 链表 链表

我们可以想象下,在构造二叉查找树的时候,如果按以下递增序列构造如:2 4 5 6 7 8,那么构造出来的查找二叉树将会是接近链表的形式,这样就增加了查找的时间复杂度。

也就是说,这课树失去了平衡。我们看看下面这个图就明白了。



这样一来,b虽然也是一颗二叉查找树,但是查找效率大大下降。所以,我们引入了平衡的二叉查找树(AVL)树。

1.平衡

关键是“平衡”这个概念的理解。

定义如下:

树中每个结点的左、右子树深度之差的绝对值不大于1

节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

下面给个例子,我们以左子树的高度减去它的右子树的高度作为平衡因子。



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.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/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: