AVL(Adelson-Velskii和Landis)树 - C语言实现(摘自数据结构与算法分析 C语言描述)

一、概述

        AVL(Adelson-Velskii和Landis)树是带有平衡条件的二叉查找树。一颗AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树(空树的高度定义为-1),如图1所示,只有左边的二叉查找树是AVL树。


图1 两颗二叉查找树,只有左边的树是AVL树

二、实现

        AVL树中的每个节点都有一个平衡因子(Balance Factor,以下用BF表示),它表示这个节点的左、右子树的高度差,也就是左子树的高度减去右子树的高度的值。AVL树上所有节点的BF值只可能是-1、0和1.反之,只要二叉查找数上一个节点的BF的绝对值大于1,则该二叉树就不是平衡二叉树。
        如何构造一颗平衡二叉树呢?动态地调整二叉查找数平衡的方法为:每插入一个节点后,首先检查是否破坏了树的平衡性,如果因插入节点而破坏了二叉查找树的平衡,则找出离插入点最近的不平衡节点,然后将该不平衡节点为根的子树进行旋转操作,我们称该不平衡节点为旋转根,让我们把必须重新平衡的节点叫做α,以该旋转根为根的子树称为最小不平衡的子树,失衡状态可归纳为4种,他们对应着4种旋转类型:
1. 对α的左儿子的左子树进行一次插入(LL);
2. 对α的左儿子的右子树进行一次插入(LR);
3. 对α的右儿子的左子树进行一次插入(RL);
4. 对α的右儿子的右子树进行一次插入(RR);
        情形1和4是关于α点的镜像对称,而2和3是关于α点的镜像对称。因此,理论上只有两种情况,当然从编程的角度来看还是四种情形。
        第一种情况是插入发生在“外边”的情况(即左-左的情况或右-右的情况),该情况通过对树的一次单旋转(single rotation,如图2)而完成调整;第二种情况是插入发生在“内部”的情形(即左-右的情况或右-左的情况),该情况通过稍微复杂些的双旋转(double rotation,如图3)来处理。

图2 单旋转(LL型)

图3 双旋转(LR型)
        旋转运算的实质,把树做任何一种旋转(LL、LR、RL、RR):
◎ 新树保持了原来的中序周游顺序;
◎ 旋转处理器中仅需改变少数指针;
◎ 而且新的子树高度为h+2,保持插入前子树的高度不变; 
◎ 原来二叉树在α节点上的其余部分(如还有的话)总是保持平衡的。
        总结:除几种情形外,编程的细节是相当简单的。为将关键字是X的一个新节点插入到一颗AVL树T中去,我们递归地将X插入到T的相应的子树(称为 T LR)中。如果的高度不变,那么插入完成。否则,如果在 T中出现高度不平衡,那么我们根据 X以及 T和中的关键字作适当的但旋转或双旋转,更新这些高度(并解决好与树的其余部分的连接),从而完成插入。
文件名:avltree.h
#ifndef _AvlTree_H

typedef int ElementType;

struct AvlNode;
typedef struct AvlNode *Position;
typedef struct AvlNode *AvlTree;

AvlTree MakeEmpty( AvlTree T );
Position Find( ElementType X, AvlTree T );
Position FindMin( AvlTree T );
Position FindMax( AvlTree T );
AvlTree Insert( ElementType X, AvlTree T );
AvlTree Delete( ElementType X, AvlTree T );
ElementType Retrieve( Position P );

static Position SingleRotateWithRight( Position K1 );
static Position SingleRotateWithLeft( Position K2 );
static Position DoubleRotateWithLeft( Position K3 );
static Position DoubleRotateWithRight( Position K4 );

ElementType Max( ElementType a, ElementType b );

void PrintElement( AvlTree T );
void PreOrder( AvlTree T );
void InOrder( AvlTree T );
void PostOrder( AvlTree T );

#endif /* _AvlTree_H */

文件名:avltree.c
#include "avltree.h"
#include "fatal.h"

struct AvlNode
{
	ElementType Element;
	AvlTree Left;
	AvlTree Right;
	int Height;
};

ElementType
Max( ElementType a, ElementType b )
{
	return (a > b ? a : b);
}

static int
Height( Position P )
{
	if ( P == NULL )
		return -1;
	else
		return P->Height;
}

AvlTree
MakeEmpty( AvlTree T )
{
	if ( T != NULL )
	{
		MakeEmpty( T->Left );
		MakeEmpty( T->Right );
		free( T );
	}
	return NULL;
}
Position
Find( ElementType X, AvlTree T )
{
	if( T == NULL )
		return NULL;
	if( X < T->Element )
		return Find( X, T->Left );
	else
		if( X > T->Element )
			return Find( X, T->Right );
		else
			return T;
}

Position
FindMin( AvlTree T )
{
	if( T == NULL )
		return NULL;
	else
		if( T->Left == NULL )
			return T;
		else
			return FindMin( T->Left );
}

Position
FindMax( AvlTree T )
{
	if( T != NULL )
		while( T->Right != NULL )
			T = T->Right;

	return T;
}

AvlTree
Delete( ElementType X, AvlTree T )
{
	printf( "Sorry; delete is unimplemented; %d remains\n", X );
	return T;
}

ElementType
Retrieve( Position P )
{
	return P->Element;
}

AvlTree
Insert ( ElementType X, AvlTree T )
{
	if ( T == NULL )
	{
		/* Create and return a one-node tree */
		T = malloc( sizeof( struct AvlNode ) );
		if ( T == NULL )
			FatalError( "Out of space!!!" );
		else
		{
			T->Element = X; T->Height = 0;
			T->Left = T->Right = NULL;
		}
	}
	else
	if ( X < T->Element )
	{
		T->Left = Insert( X, T->Left );
		if ( Height( T->Left ) - Height( T->Right ) == 2 )
			if ( X < T->Left->Element )
				T = SingleRotateWithLeft( T );
			else
				T = DoubleRotateWithLeft( T );
	}
	else
	if ( X > T->Element )
	{
		T->Right = Insert( X, T->Right );
		if ( Height( T->Right ) - Height( T->Left ) == 2 )
			if ( X > T->Right->Element )
				T = SingleRotateWithRight( T );
			else
				T = DoubleRotateWithRight( T );
	}
	/* Else X is in the tree already;we'll do nothing */
	T->Height = Max( Height( T->Left ), Height( T->Right ) ) + 1;
	return T;
}

/* This function can be called only if K2 has a left child */
/* Perform a rotate between a node(K2) and its left child */
/* Update heights, then return new root */
static Position
SingleRotateWithLeft( Position K2 )
{
	Position K1;

	K1 = K2->Left;
	K2->Left = K1->Right;
	K1->Right = K2;

	K2->Height = Max( Height( K2->Left ), 
		Height( K2->Right ) ) +1;
	K1->Height = Max( Height( K1->Left ), 
		K2->Height ) +1;

	return K1; /* New root */
}

/* This function can be called only if K1 has a right child */
/* Perform a rotate between a node(K1) and its right child */
/* Update heights, then return new root */
static Position
SingleRotateWithRight( Position K1 )
{
	Position K2;

	K2 = K1->Right;
	K1->Right = K2->Left;
	K2->Left = K1;

	K1->Height = Max( Height( K1->Left ), 
		Height( K1->Right ) ) + 1;
	K2->Height = Max( Height( K2->Right ), 
		K1->Height ) + 1;

	return K2; /* New root */
}

/* This function can be called only if K3 has a left */
/* child and K3's left child has a right child */
/* Do the left-right double rotation */
/* Update heights,then return new root */
static Position
DoubleRotateWithLeft( Position K3 )
{
	/* Rotate between K1 and K2 */
	K3->Left = SingleRotateWithRight( K3->Left );

	/* Rotate between K3 and K2 */
	return SingleRotateWithLeft( K3 );
}

/* This function can be called only if K1 has a right */
/* child and K1's right child has a left child */
/* Do the left-right double rotation */
/* Update heights,then return new root */
static Position
DoubleRotateWithRight( Position K1 )
{
	/* Rotate between K3 and K2 */
	K1->Right = SingleRotateWithLeft( K1->Right );

	/* Rotate between K1 and K2 */
	return SingleRotateWithRight( K1 );
}

void
PrintElement( AvlTree T )
{
	printf( "%3d ", Retrieve( T ) );
}

void
PreOrder( AvlTree T )
{
	if (T != NULL )
	{
		PrintElement( T );
		PreOrder( T->Left );
		PreOrder( T->Right );
	}	
}

void 
InOrder( AvlTree T )
{
	if (T != NULL )
	{
		InOrder( T->Left );
		PrintElement( T );
		InOrder( T->Right );
	}
}

void
PostOrder( AvlTree T )
{
	if ( T != NULL )
	{
		PostOrder( T->Left );
		PostOrder( T->Right );
		PrintElement( T );
	}
}

文件名:main.c
#include "avltree.h"
#include <stdio.h>

int main()
{
	AvlTree T = NULL;
	int i, n;
	ElementType tmp;
	printf( "Number of Elements:" );
	scanf( "%d", &n );
	for ( i = 0; i < n; i++)
	{
		scanf( "%d", &tmp );
		T = Insert( tmp, T );
	}
 	printf( "\nPreOrder :" );
        PreOrder( T );
	printf( "\nInOrder  :" );
	InOrder( T );
	printf( "\nPostOrder:" );
	PostOrder( T );
        printf( "\n" );
	return 0;
}

附录:上述代码中用到了Error、FatalError等函数,其实现如下(即fatal.h文件):
#include <stdio.h>
#include <stdlib.h>

#define Error( Str )        FatalError( Str )
#define FatalError( Str )   fprintf( stderr, "%s\n", Str ), exit( 1 )

备注:本文摘自《数据结构与算法分析 C语言描述 Mark Allen Weiss著》,代码经gcc编译测试通过。

附件下载:http://download.csdn.net/detail/shuxiao9058/4212421#avltree_20120401.tar.gz




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值