linux 自平衡AVL树

简介

仔细观察BST会发现,虽然它有良好的“搜索”特性,也就是可以利用其节点之前的大小关系,很容易地从根节点开始往下走找到我们所要的节点,但它无法保证这种搜索所需要的时间长短,因为建立BST时节点的随机性可能会导致它极其“不平衡”。
12
如上图所示是一棵二叉树,显然左轻右重,事实上这棵树已经退化成了一条链表,在这棵树中搜索某一个节点的时间复杂度和链表是一样的。

这种左轻右重或者左重右轻的长短腿的情形,就是所谓的不平衡;一棵树不平衡,那么他的搜索性能将会受到影响。具体来讲,当树保持平衡时,其搜索时间复杂度是O(log2n);当树退化成链表时,其搜索时间复杂度是O(n),其他情况下树的平均搜索时间复杂度就介于这两者之间。现在的目标就是要升级我们的BST,使之带有自平衡的特性,当它发现自己的左腿长或右腿长时,会及时调整,保持平衡。

要达到此目的,首先需要量化所谓的“平衡”,其严格数学定义是:在一棵树中,如果其任意一个节点的左右子树的高度差绝对值小于或等于1,那么它就是平衡的。

下面是AVL树的算法实现,包括了节点的创建、插入、删除、旋转等算法。

算法实现

head4tree.h

//////////////////////////////////////////////////////////////////
//  Description: 本文件为二叉树核心头文件。
//               任何使用本二叉树结构算法的程序,在包含本头文件之前
//               都需要将如下宏定义成二叉树节点需要表达的数据类型:
//
//                     TREE_NODE_DATATYPE
//
//               否则二叉树的节点数据类型一律默认为 int
//////////////////////////////////////////////////////////////////

#ifndef _HEAD4TREE_H_
#define _HEAD4TREE_H_

/*
 * Any application applying this linked-tree data structure should
 * define the macro "TREE_NODE_DATATYPE" before include this head
 * file, otherwise, the macro will be defined to 'int' as follow.
 *
*/

#ifndef TREE_NODE_DATATYPE
#define TREE_NODE_DATATYPE int
#endif

#include "commonheader.h"

#define MAX(a, b) ({ \
		typeof(a) _a = a; \
		typeof(b) _b = b; \
		(void)(&_a == &_b);\
		_a > _b? _a : _b; \
		})

typedef TREE_NODE_DATATYPE tn_datatype;


typedef struct _tree_node
{
	tn_datatype data;
	struct _tree_node *lchild;
	struct _tree_node *rchild;

#ifdef AVL
	int height;
#endif

}treenode, *linktree;

void pre_travel(linktree, void (*handler)(linktree));
void mid_travel(linktree, void (*handler)(linktree));
void post_travel(linktree, void (*handler)(linktree));
void level_travel(linktree, void (*handler)(linktree));

linktree bst_insert(linktree root, linktree new);
linktree bst_remove(linktree root, tn_datatype data);
linktree bst_find(linktree root, tn_datatype data);

#ifdef AVL
linktree avl_insert(linktree root, linktree new);
linktree avl_remove(linktree root, tn_datatype data);

linktree avl_rotate_left (linktree root);
linktree avl_rotate_right(linktree root);
linktree avl_rotate_leftright(linktree root);
linktree avl_rotate_rightleft(linktree root);

static int height(linktree root)
{
	return root==NULL ? 0 : root->height;
}
#endif

static linktree new_node(tn_datatype data)
{
	linktree new = malloc(sizeof(treenode));
	if(new != NULL)
	{
		new->data = data;
		new->lchild = NULL;
		new->rchild = NULL;

		#ifdef AVL
		new->height = 1;
		#endif

	}
	return new;
}

#endif

avl.c

////////////////////////////////////////////////////////////////////

//  Description: AVL算法实现代码

////////////////////////////////////////////////////////////////////

#include "head4tree.h"
#include "drawtree.h"

linktree avl_rotate_right(linktree root)
{
	linktree tmp = root->lchild;
	root->lchild = tmp->rchild;
	tmp->rchild = root;
	
	root->height = MAX(height(root->lchild), height(root->rchild)) + 1;
	tmp->height = MAX(height(tmp->lchild), root->height) + 1;

	return tmp;
}

linktree avl_rotate_left(linktree root)
{
	linktree tmp = root->rchild;
	root->rchild = tmp->lchild;
	tmp->lchild = root;
	
	root->height = MAX(height(root->lchild), height(root->rchild)) + 1;
	tmp->height = MAX(root->height, height(tmp->rchild)) + 1;

	return tmp;
}

linktree avl_rotate_leftright(linktree root)
{
	root->lchild = avl_rotate_left(root->lchild);
	return avl_rotate_right(root);
}

linktree avl_rotate_rightleft(linktree root)
{
	root->rchild = avl_rotate_right(root->rchild);
	return avl_rotate_left(root);
}

linktree avl_insert(linktree root, linktree new)
{
	if(root == NULL)
		return new;


	if(new->data < root->data)
		root->lchild = avl_insert(root->lchild, new);
	else if(new->data > root->data)
		root->rchild = avl_insert(root->rchild, new);
	else
	{
		printf("%d is already exist.\n", new->data);
	}


	if(height(root->lchild) - height(root->rchild) == 2)
	{
		if(new->data < root->lchild->data)
			root = avl_rotate_right(root);
		else if(new->data > root->lchild->data)
			root = avl_rotate_leftright(root);
	}

	else if(height(root->rchild) - height(root->lchild) == 2)
	{
		if(new->data > root->rchild->data)
			root = avl_rotate_left(root);
		else if(new->data < root->rchild->data)
			root = avl_rotate_rightleft(root);
	}


	root->height = MAX(height(root->lchild), height(root->rchild)) + 1;
	return root;
}

linktree avl_remove(linktree root, tn_datatype data)
{
	if(root == NULL)
		return NULL;

	if(data < root->data)
		root->lchild = avl_remove(root->lchild, data);
	else if(data > root->data)
		root->rchild = avl_remove(root->rchild, data);
	else
	{
		linktree p;

		if(root->lchild != NULL)
		{
			for(p=root->lchild; p->rchild!=NULL; p=p->rchild){;}
			root->data = p->data;
			root->lchild = avl_remove(root->lchild, p->data);
		}
		else if(root->rchild != NULL)
		{
			for(p=root->rchild; p->lchild!=NULL; p=p->lchild){;}
			root->data = p->data;
			root->rchild = avl_remove(root->rchild, p->data);
		}
		else
		{
			free(root);
			return NULL;
		}
	}

	if(height(root->lchild) - height(root->rchild) == 2)
	{
		if(height(root->lchild->rchild)-height(root->lchild->rchild) == 1)
			root = avl_rotate_leftright(root);
		else
			root = avl_rotate_right(root);
	}
	else if(height(root->rchild) - height(root->lchild) == 2)
	{
		if(height(root->rchild->lchild)-height(root->rchild->rchild) == 1)
			root = avl_rotate_rightleft(root);
		else
			root = avl_rotate_left(root);
	}

	root->height = MAX(height(root->lchild), height(root->rchild)) + 1;
	return root;
}

int main(void)
{
	linktree root = NULL;

	printf("输入大于0的数插入节点\n");
	printf("输入小于0的数删除节点\n");
	printf("输入0退出程序\n");

	int n;
	while(1)
	{
		scanf("%d", &n);

		if(n > 0)
		{
			linktree new = new_node(n);
			root = avl_insert(root, new);
		}
		else if(n < 0)
		{
			root = avl_remove(root, -n);
		}
		else
			break;

		draw(root);
		system("firefox -new-tab *.html &");
	}
	system("rm *.html");

	return 0;
}

示例

直接使用上面的文件,上述的main函数实现了以下功能:
1、输入一个正整数,则插入该节点
2、输入一个负整数,则删除其绝对值对应的节点
3、输入0,退出程序
4、退出程序前在网页上画出该二叉树

运行效果如下:

zzc@zzc-virtual-machine:~/share/example/数据结构$ ./avl
输入大于0的数插入节点
输入小于0的数删除节点
输入0退出程序
1
2
3
4
54
5
8
6
2
2 is already exist.
8
8 is already exist.
9
10
0

在网页上显示该二叉树:
12

注意事项

1、上述示例程序中使用了draw函数,用于在网页上画出二叉树。
2、上述示例用到的源代码(一些头文件和c文件)放在以下博文中:
https://blog.csdn.net/gogo0707/article/details/124855232
3、AVL树也是BST树,所以查找某一个节点时直接使用BST查找算法即可

总结

本文介绍了AVL树的基本概念,进行了算法的实现,并通过示例程序演示了二叉树如何实现自平衡。
后续我有时间再慢慢补充有关AVL树的知识点和一些可能遇到的问题。

posted @ 2022-05-20 03:19  李星云姬如雪  阅读(26)  评论(0编辑  收藏  举报