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/1s246http://t.csdn.cn/1s246本文基于vs2010旗舰版编译器,使用其他编译器可能需要改动代码才可以运行,如果有错误,欢迎讨论。