AVL树的创建与性能分析

        BST查找树是一颗有规律的二叉树,其左子树的关键字值必定比根节点小,右子树的关键字值必定比根节点大。但是BST的查找效率有时候并不优秀,因为它并不是总是平衡的。所以引出平衡的BST查找树,即AVL查找树。

        AVL查找树的性能较好,时间复杂度可以达到O(logN);

        下面给出一个具体的实例实现AVL树查找并计算其平均比较次数ASL。

        数据储存信息为Info结构体,AVL节点为AVL结构体,如下:

struct Info {
    char name[30];
    char sex[3];
    char number[12];
    char college[30];
    struct AVL* tree_node;
};
struct AVL {
    struct AVL* left;
    struct AVL* right;
    struct AVL* parent;
    Info* data;
    int height; //结点高度
};

         在创建AVL树的过程中,需要将节点一个一个插入,并判断平衡因子是否超过平衡值。所以先给出一些预备函数。

int max(int a, int b) {
    return a > b ? a : b;
}
int height(struct AVL* node) {
    if (node == NULL) return 0;
    else return node->height;
}
int get_balance(struct AVL* node) {
    if (node == NULL) return 0;
    return height(node->left) - height(node->right);
}
AVL* new_node(Info* data) 
{
    AVL* node = (AVL*)malloc(sizeof(AVL));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;
    node->height = 1;
    return node;
}

         如果判断完平衡因子后需要调整树的结构,则会涉及到两种基本的方式,左旋还有右旋,这也是整个AVL树创建的重点和难点。

        思想如下:如果需要左旋,则先将开始调整的节点传到调整函数。先将该节点的右子树整个保存下来,然后将原来右孩子的左孩子接到该节点的右边,成为它的右孩子,然后判断该节点是否为根节点,如果是,则将其右孩子变为新的根节点,然后调整平衡因子。如果不是根节点,则调整该节点的父亲为其原来的右孩子,同时将右孩子接到该节点的原父亲下,成为其左孩子或者右孩子。然后调整平衡因子。右旋同理。

struct AVL* left_rotate(struct AVL* node) //左旋
{
    struct AVL* right_child = node->right;

    node->right = right_child->left;
    if (right_child->left != NULL)
        right_child->left->parent = node;

    right_child->parent = node->parent;

    if (node->parent == NULL) {
        // root node
    }
    else if (node == node->parent->left) {
        node->parent->left = right_child;
    } 
    else {
        node->parent->right = right_child;
    }

    right_child->left = node;
    node->parent = right_child;

    node->height = 1 + max(height(node->left), height(node->right));
    right_child->height = 1 + max(height(right_child->left), height(right_child->right));

    return right_child;
}
struct AVL* right_rotate(AVL* node) {
    AVL* left_child = node->left;
    node->left = left_child->right;

    if (left_child->right != NULL)
        left_child->right->parent = node;

    left_child->parent = node->parent;

    if (node->parent == NULL) {
        // root node
    }
    else if (node == node->parent->right) {
        node->parent->right = left_child;
    } 
    else {
        node->parent->left = left_child;
    }

    left_child->right = node;
    node->parent = left_child;

    node->height = 1 + max(height(node->left), height(node->right));
    left_child->height = 1 + max(height(left_child->left), height(left_child->right));

    return left_child;
}

AVL创建方式为一个一个节点插入创建,其插入函数也很重要。

思想如下:

        把新节点和信息都传入插入函数,基于先序遍历寻找插入位置并插入,然后更新平衡因子然后判断是否需要调整,需要如何调整。

AVL* insert(AVL* node, Info* data) {
    if (node == NULL) 
        return new_node(data);
    if (strcmp(data->number, node->data->number) < 0)
        node->left = insert(node->left, data);
    else if (strcmp(data->number, node->data->number) > 0)
        node->right = insert(node->right, data);
    else //出现相同关键字
        return node;

    node->height = 1 + max(height(node->left), height(node->right));
    int balance = get_balance(node);

    if (balance > 1 && strcmp(data->number, node->left->data->number) < 0){
        return right_rotate(node);
    }
    if (balance < -1 && strcmp(data->number, node->right->data->number) > 0) {
        return left_rotate(node);
    }
    if (balance > 1 && strcmp(data->number, node->left->data->number) > 0) {
        node->left = left_rotate(node->left);
        return right_rotate(node);
    }
    if (balance < -1 && strcmp(data->number, node->right->data->number) < 0) {
        node->right = right_rotate(node->right);
        return left_rotate(node);
    }
    return node;
}

         查找函数相对简单,基于先序递归查找。

AVL* search(AVL* node, char number[12]) 
{
    if (node == NULL)
	{
		AVLBJ_sum++;
        return NULL;
	}
    if (strcmp(number, node->data->number) == 0)
	{
		AVLBJ_sum++;
        return node;
	}
    else if (strcmp(number, node->data->number) < 0)
	{
		AVLBJ_sum++;
        return search(node->left, number);
	}
    else
	{
		AVLBJ_sum++;
        return search(node->right, number);
	}
}

计算ASL,根据定义,需要计算每一层的节点个数,基于层次遍历整棵树即可。

float AVL_asl(AVL *T)//计算并返回AVL查找成功的ASL
{
	AVL *p;
	AVL *qu[3000];   //定义队列,进行层次遍历。
	int front,rear;
	float ASL=0,n=0;
	front=rear=0;
	int count[3000]={0},i=1,j=1;  //用来记录每一层有多少个结点。
	qu[rear]=T;
	rear++;
	count[i]=1;
	i++;
	while(front!=rear)
	{
		p=qu[front];
		front=(front+1)%3000;
		if(p->left!=NULL)
		{
			qu[rear]=p->left;
			rear=(rear+1)%3000;
			count[i]++;
		}
		if(p->right!=NULL)
		{
			qu[rear]=p->right;
			rear=(rear+1)%3000;
			count[i]++;
		}
		if(j==count[i-1])
		{
			i++;
			j=1;
		}
		else
			j++;		
	}
	for(i=1;count[i]!=0;i++)
		n=n+count[i];
	for(i=1;count[i]!=0;i++)
		ASL=ASL+i*count[i];
	ASL=(1.0/n)*ASL;
	return ASL;
}

整个函数如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int AVLBJ_sum=0;
#define N 2438
struct Info {
    char name[30];
    char sex[3];
    char number[12];
    char college[30];
    struct AVL* tree_node;
};
struct AVL {
    struct AVL* left;
    struct AVL* right;
    struct AVL* parent;
    Info* data;
    int height; //结点高度
};
int max(int a, int b) {
    return a > b ? a : b;
}
int height(struct AVL* node) {
    if (node == NULL) return 0;
    else return node->height;
}
int get_balance(struct AVL* node) {
    if (node == NULL) return 0;
    return height(node->left) - height(node->right);
}
AVL* new_node(Info* data) 
{
    AVL* node = (AVL*)malloc(sizeof(AVL));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;
    node->height = 1;
    return node;
}
struct AVL* left_rotate(struct AVL* node) //左旋
{
    struct AVL* right_child = node->right;

    node->right = right_child->left;
    if (right_child->left != NULL)
        right_child->left->parent = node;

    right_child->parent = node->parent;

    if (node->parent == NULL) {
        // root node
    }
    else if (node == node->parent->left) {
        node->parent->left = right_child;
    } 
    else {
        node->parent->right = right_child;
    }

    right_child->left = node;
    node->parent = right_child;

    node->height = 1 + max(height(node->left), height(node->right));
    right_child->height = 1 + max(height(right_child->left), height(right_child->right));

    return right_child;
}
struct AVL* right_rotate(AVL* node) {
    AVL* left_child = node->left;
    node->left = left_child->right;

    if (left_child->right != NULL)
        left_child->right->parent = node;

    left_child->parent = node->parent;

    if (node->parent == NULL) {
        // root node
    }
    else if (node == node->parent->right) {
        node->parent->right = left_child;
    } 
    else {
        node->parent->left = left_child;
    }

    left_child->right = node;
    node->parent = left_child;

    node->height = 1 + max(height(node->left), height(node->right));
    left_child->height = 1 + max(height(left_child->left), height(left_child->right));

    return left_child;
}
AVL* insert(AVL* node, Info* data) {
    if (node == NULL) 
        return new_node(data);
    if (strcmp(data->number, node->data->number) < 0)
        node->left = insert(node->left, data);
    else if (strcmp(data->number, node->data->number) > 0)
        node->right = insert(node->right, data);
    else //出现相同关键字
        return node;

    node->height = 1 + max(height(node->left), height(node->right));
    int balance = get_balance(node);

    if (balance > 1 && strcmp(data->number, node->left->data->number) < 0){
        return right_rotate(node);
    }
    if (balance < -1 && strcmp(data->number, node->right->data->number) > 0) {
        return left_rotate(node);
    }
    if (balance > 1 && strcmp(data->number, node->left->data->number) > 0) {
        node->left = left_rotate(node->left);
        return right_rotate(node);
    }
    if (balance < -1 && strcmp(data->number, node->right->data->number) < 0) {
        node->right = right_rotate(node->right);
        return left_rotate(node);
    }
    return node;
}
AVL* search(AVL* node, char number[12]) 
{
    if (node == NULL)
	{
		AVLBJ_sum++;
        return NULL;
	}
    if (strcmp(number, node->data->number) == 0)
	{
		AVLBJ_sum++;
        return node;
	}
    else if (strcmp(number, node->data->number) < 0)
	{
		AVLBJ_sum++;
        return search(node->left, number);
	}
    else
	{
		AVLBJ_sum++;
        return search(node->right, number);
	}
}
float AVL_asl(AVL *T)//计算并返回AVL查找成功的ASL
{
	AVL *p;
	AVL *qu[3000];   //定义队列,进行层次遍历。
	int front,rear;
	float ASL=0,n=0;
	front=rear=0;
	int count[3000]={0},i=1,j=1;  //用来记录每一层有多少个结点。
	qu[rear]=T;
	rear++;
	count[i]=1;
	i++;
	while(front!=rear)
	{
		p=qu[front];
		front=(front+1)%3000;
		if(p->left!=NULL)
		{
			qu[rear]=p->left;
			rear=(rear+1)%3000;
			count[i]++;
		}
		if(p->right!=NULL)
		{
			qu[rear]=p->right;
			rear=(rear+1)%3000;
			count[i]++;
		}
		if(j==count[i-1])
		{
			i++;
			j=1;
		}
		else
			j++;		
	}
	for(i=1;count[i]!=0;i++)
		n=n+count[i];
	for(i=1;count[i]!=0;i++)
		ASL=ASL+i*count[i];
	ASL=(1.0/n)*ASL;
	return ASL;
}
int main()
{
	AVL* root = NULL;
	char s[12]={'0'};      //需要查找的元素
    Info *arr,t;
	int i=0;
	float AVL_ASL;
	FILE *fp;
	arr=new Info[N];
	fp=fopen("data.txt","r");
	if(!fp)
	{
		printf("文件打开失败,程序结束!");
		exit(0);
	}
	while(!feof(fp))
	{
		fscanf(fp,"%s %s %s %s",arr[i].name,arr[i].sex,arr[i].number,arr[i].college);
		i++;
	}
	for(int i=0;i<N;i++)
	{
		root = insert(root, &arr[i]);
	}
	AVL_ASL=AVL_asl(root);
	printf("该AVL查找树的ASL为:%f\n\n",AVL_ASL);
	printf("请输入查询的电话号码:");
	scanf("%s",s);
	AVL* node1 = search(root, s);
	if (node1 == NULL) 
	{
        printf("电话号为%s的人没有找到.\n",s);
		printf("AVL共比较了%d次。\n",AVLBJ_sum);
    } 
	else 
	{
        printf("电话号为 %s 是 %s.其信息如下:\n",s, node1->data->name);
		printf("姓名\t性别\t电话号码\t学院\n");
		printf("\n%s\t%s\t%s\t%s\n",node1->data->name,node1->data->sex,node1->data->number,node1->data->college);
		printf("AVL共比较了%d次。\n",AVLBJ_sum);
    }
	fclose(fp);
	return 0;
}

运行结果如下图:

使用的数据文件为”data.txt“,具体格式见哈希查找博客:

http://t.csdn.cn/1s246icon-default.png?t=N5F7http://t.csdn.cn/1s246本文基于vs2010旗舰版编译器,使用其他编译器可能需要改动代码才可以运行,如果有错误,欢迎讨论。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Math-L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值