AVL树(c实现)

本文详细解析了AVL树的C语言实现,包括基础的二叉平衡树概念,AVL树的旋转操作,以及如何通过中根遍历来构建AVL树。重点阐述了AVL树在插入节点时的平衡因子调整机制和树高计算过程,通过实例展示了AVL树的插入过程及其平衡状态的变化。
摘要由CSDN通过智能技术生成

最近阅读java的TreeMap源码,发觉是使用红黑树实现内部的数据管理,所以开始学习编写红黑树。然后发现要想理解红黑树还是要从基础的二叉平衡树的开始。于是,最终决定拿AVL树开刀。

网络上有很多AVL树的各种语言实现版本,个人偏爱c的绝对控制性编程,所以选择了c实现。令人庆幸的是,一个非常简易的AVL树c实现被我找到。现在就在其基础上加上个人理解把AVL树实现描述如下:

#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
	int data;
	int height;
	struct Node *lchild,*rchild;
} Node;

int Max(int a,int b)
{
	return a>b?a:b;
}
int height(Node* pNode)
{
	if(pNode == NULL)
	{
		return -1;
	}
	return pNode->height;
}
Node* single_rotate_with_left(Node* pNode)
{
	Node* tmp;

	tmp = pNode->lchild;
	pNode->lchild = tmp->rchild;
	tmp->rchild = pNode;
	//结点的位置改变,需更新结点的高度
	//结点的高度根据孩子的计算得到:
	//pNode的左右孩子分别是pNode->lchild,pNode->rchild
	//tmp的孩子:tmp->lchild,pNode
	pNode->height = Max(height(pNode->lchild),height(pNode->rchild)) + 1;
	tmp->height = Max(height(tmp->lchild),pNode->height) + 1;
	return tmp;
}

Node* single_rotate_with_right(Node* pNode)
{
	Node* tmp;
	tmp = pNode->rchild;
	pNode->rchild = tmp->lchild;
	tmp->lchild = pNode;
	//原理同上
	pNode->height = Max(height(pNode->lchild),height(pNode->rchild)) + 1;
	tmp->height = Max(height(tmp->rchild),pNode->height) + 1;
	return tmp;
}

Node* double_l(Node* pNode)
{
	pNode->lchild = single_rotate_with_right(pNode->lchild);

	return single_rotate_with_left(pNode);
}

Node* double_r(Node* pNode)
{
	pNode->rchild = single_rotate_with_left(pNode->rchild);
	return single_rotate_with_right(pNode);
}

Node* insert(int d,Node* pNode)
{
	if(pNode == NULL)
	{//如果删除判断平衡因子的语句和更新树高度的语句,则此insert完全是一个BST的插入操作
	 //所以从此函数,可以看出AVL树脱胎自BST
		pNode = (Node*)malloc(sizeof(Node));
		pNode->data = d;
		pNode->height = 0;
		pNode->lchild = pNode->rchild = NULL;
	} else if (d <  pNode->data) { //插入左子树
		pNode->lchild = insert(d,pNode->lchild);

		if(height(pNode->lchild) - height(pNode->rchild) == 2) //not balence
		{
			if(d < pNode->lchild->data)
			{
				//插入到左子树,做单旋转
				pNode = single_rotate_with_left(pNode);
			} else {
				pNode = double_l(pNode);
			}
		}
	} else if (d > pNode->data) {
		pNode->rchild = insert(d,pNode->rchild);

		if(height(pNode->rchild) - height(pNode->lchild) == 2) {
			if(d > pNode->rchild->data)
			{
				pNode = single_rotate_with_right(pNode);
			} else {
				pNode = double_r(pNode);
			}
		}
	}

	pNode->height = Max(height(pNode->lchild),height(pNode->rchild)) + 1;
	return pNode;
}
void print(Node* root)
{//中根遍历二叉树
	if(root == NULL)
	{
		return ;
	}
	print(root->lchild);
	fprintf(stdout,"%d ",root->data);
	print(root->rchild);
}

int main(void)
{
	int i;
	Node* root = NULL;

	int a[] = {1,2,3,4,5};
	const int len = sizeof(a)/sizeof(int);
	
	for(;i<len;i++)
	{
		root = insert(a[i],root);
	}

	print(root);
	return 0;
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>


以上代码通过测试。

详解依次插入1,2,3,4,5时的建树过程:

 1、插入1:

       1

2、插入2:

       1

            \

              2

3、插入3:

       1                                               1                                                                    2

           \                    ==>                      \                             ==>                        /          \

              2                                               2                                                      1            3

                                                                    \ 

                                                                       3

4、插入4:

               2                                         

           /        \

         1          3             

                         \

                          4

5 、插入5:

               2                                                 2

           /        \                                         /        \

         1          3             ==>                1          4

                        \                                            /       \

                          4                                      3          5

                             \ 

                               5

此时构建出AVL树,通过gdb调试,我们可以验证最终的树即是第5步最后的那棵树。从图中,我们可知道建树过程中共进行够两次左旋转。


旋转过程中,如果能够理解树的高度计算,那么可以说AVL树已经理解。

下面使用一个实例来说明树的高度计算过程:

例:顺序插入3,2,1,构建AVL树。

                                                3   (h3=1)                          3(h3=1)                                      2 (h2=1)                                    2(h2=1)

       3  (h3=0)       ==>          /                         ==>         /                              ==>                /          \                       ==>          /           \

                                             2  (h2=0)                        2 (h2=1)                                  1(h1=0)     3(h3=1)                    1(h1=0)    3(h3=0)

                                                                                   /

                                                                                 1(h1=0)


过程详解:

刚插入的结点高度默认为0, 插入2后insert方法返回,程序进入3结点的insert方法,因为平衡,所以程序直接跳到更新高度的代码行,更新3这个结点的高度为1(具体方法,参见代码);

插入1结点后,返回到2结点的insert方法,此时平衡因子为1,所以直接更新2的高度为1;接着进入3结点的insert方法,此时3的高度还是1,但是3结点的左结点高度1,减去右结点高度-1(参见height函数),平衡因子等于2,

此时平衡被打破,需进行右旋转;旋转后的图,即是过程3之后的图。根据右旋转方法,可知:先计算3结点的高度h3,而其高度是左孩子和右孩子中最大值加1,所以此时h3=-1+1=0;然后计算2结点的高度h2,

2结点的高度由它的左右孩子值加1决定(父结点的高度一定等于最大孩子高度加1),此时h1和h3都等于0,所以h2=0+1=1。


至此,整个高度的更新过程描述完毕。











 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值