二叉树之AVL树

今天来细说下 AVL 树,并结合图示代码来认识并掌握 AVL 树的创建,删除,体会下 AVL 特有的自平衡性。

目录:

AVL 概念

AVL 的旋转

AVL 树旋转实现

AVL 树代码实现(C语言)

测试实例

AVL 概念

AVL 也是二叉树的一种,所以符合所有二叉树应有的特性,以及遍历方法

与二叉树不同的是,AVL树是一颗平衡树:

节点 root,左子节点的高度 与 右子节点的高度,最大差值 = 1

下面看看 AVL 树的示例:

理论上来讲 root 节点深度 = 1,依次增加,这里相反是因为后续代码初始节点深度 = 1

 再来看看 非 AVL 树的示例,把上图的节点 6 去掉

可见针对 节点 5 来讲,左子树深度=3, 右子树深度=1,已经超过最大深度 1,所以不是 AVL 树

AVL 树的旋转

我们在操作树一定发生:添加或者删除节点,AVL 的状态可能会被破坏,基于不同形态的 非 AVL 树,我们有以下 四种姿态 需要通过旋转重回 AVL 形态

如果把AVL树(平衡树)看做一个人,左右子节点就是左右手提的东西

左手重或者右手重就会失去平衡,失去平衡的原因就是姿态

比如:LR,左子节点深度过大,且是因为左子节点下的右子节点(多余)造成的

形态一:LL 树 (Left-Left)

节点 6 的左右不平衡,原因是

1. Left 节点 4 更重

2.  造成节点 4 更重的原因是 4 的 Left 节点 3 还有子节点

形态二:RR 树 (Right-Right)

节点 3 的左右不平衡,原因是

1. Right 节点 6 更重

2.  造成节点 6 更重的原因是 8 的 Right 节点 8 还有子节点

形态三:LR 树 (Left-Right)

节点 8 的左右不平衡,原因是

1. Left 节点 4 更重

2.  造成节点 4 更重的原因是 4 的 Right 节点 5 还有子节点

形态四:RL 树 (Right-Left)

节点 3 的左右不平衡,原因是

1. Right 节点 8 更重

2.  造成节点 8 更重的原因是 8 的 Left 节点 5 还有子节点

AVL 树旋转实现

形态一:LL 树旋转 

既然左手 K1 重了,那么左手 K1 就当脑袋,脑壳 K2 顺势变成右手,这样占用了 新脑袋的右手,原本 K1 的右手就只能丢给 K2 的左手

/* LL Rotation AVL Tree 
** Parameters:
**		root - avl tree
**
		k2						k1
	k1		c	-LL->		a		k2
  a    b				  d	       b   c
d  
*/
PNode ll_rotation(PNode k2)
{
	PNode k1;
	
	k1 = k2->left;
	k2->left = k1->right;
	k1->right = k2;
	
	k2->deep = MAX( DEEP(k2->left), DEEP(k2->right) ) + 1;
	k1->deep = MAX( DEEP(k1->left), k2->deep ) + 1;
	
	return k1;
}

形态二:RR 树旋转  

 既然右手 K2 重了,那么右手 K2 就当脑袋,脑壳 K1 顺势变成左手,这样占用了 新脑袋的左手,原本 K2 的左手就只能丢给 K1 的右手

/* RR Rotation AVL Tree 
** Parameters:
**		root - avl tree
**
		k1						k2
	a		k2	 -RR->		k1		c
		  b    c		  a   b		  d
				 d
*/
PNode rr_rotation(PNode k1)
{
	PNode k2;
	
	k2 = k1->right;
	k1->right = k2->left;
	k2->left = k1;
	
	k1->deep = MAX( DEEP(k1->left), DEEP(k1->right) ) + 1;
	k2->deep = MAX( DEEP(k2->right), k1->deep ) + 1;
	
	return k2;
}

形态三:LR 树旋转

需经过两次变化,RR + LL ,按照右到左(LR)名字

/* LR Rotation AVL Tree 
** Parameters:
**		root - avl tree
**
		k3						k3						k2
	k1		d	--RR->		k2		d		--LL->	 k1     k3
  a	  k2				  k1   c				   a   b   c   d
     b  c				a    b
*/
PNode lr_rotation(PNode k3)
{
	PNode k1, k2;
	
	k1 = k3->left;
	k2 = rr_rotation(k1);
	k3->left = k2;
	
	k2 = ll_rotation(k3);
	
	return k2;
}

形态四:RL 树旋转

 需经过两次变化,LL+RR ,按照右到左(RL)名字

/* LR Rotation AVL Tree 
** Parameters:
**		root - avl tree
**
		k3						k3						k2
	k1		d	--RR->		k2		d		--LL->	 k1     k3
  a	  k2				  k1   c				   a   b   c   d
     b  c				a    b
*/
PNode lr_rotation(PNode k3)
{
	PNode k1, k2;
	
	k1 = k3->left;
	k2 = rr_rotation(k1);
	k3->left = k2;
	
	k2 = ll_rotation(k3);
	
	return k2;
}

AVL 树代码实现

有个限定:

1. 左子节点值 < root 节点值 < 右子节点值

2. 不存在相同节点值

数据结构

typedef struct AVLTreeNode{
	int val;
	int deep;
	struct AVLTreeNode *left;
	struct AVLTreeNode *right;
}Node, *PNode;

 获取树深

#define DEEP(node) ( node == NULL ? 0 : node->deep )

/* Get AVL Tree's Deep
** Parameters:
**		root - avl tree
** Note: Deep incream from leaf(1) to root
**
*/
int avltree_get_deep(PNode root)
{
	return DEEP(root);
}

获取树的最小值的节点

/* Get AVL Tree's Minimum Val Node
** Parameters:
**		root - avl tree
*/
PNode avltree_get_min(PNode root)
{
	if(root == NULL)
		return NULL;
	
	while(root->left)
		root = root->left;
	
	return root;
}

 获取树的最大值的节点 

/* Get AVL Tree's Minimum Val Node
** Parameters:
**		root - avl tree
*/
PNode avltree_get_min(PNode root)
{
	if(root == NULL)
		return NULL;
	
	while(root->left)
		root = root->left;
	
	return root;
}

 创建树的节点

/* Create AVL Tree Node
** Parameters:
**		val - val
**		left - left child
**		right - right child
*/
PNode avltree_create_node(int val, PNode left, PNode right)
{
	PNode newNode = (PNode)malloc(sizeof(Node));
	if(newNode == NULL)
		return NULL;
	
	newNode->val = val;
	newNode->deep = 1;
	newNode->left = left;
	newNode->right = right;
	
	return newNode;
}

查找树的节点 

/* Search Node of AVL Tree
** Parameters:
**		root - avl tree
**		val - node's val
*/
PNode avltree_search_node(PNode root, int val)
{
	if(root == NULL || root->val == val)
		return root;
		
	if(val < root->val)
		return avltree_search_node(root->left, val);
	else
		return avltree_search_node(root->right, val);
}

 插入节点到树,如果没有根节点则创建根节点

/* Insert New Node to AVL Tree
** Parameters:
**		root - avl tree
**		val - node to insert
*/
PNode avltree_insert_node(PNode root, int val)
{
	if(root == NULL)	
	{
		root = avltree_create_node(val, NULL, NULL);
		if(root == NULL)
			return NULL;
	}
	else if(val < root->val)
	{
		root->left = avltree_insert_node(root->left, val);
		
		if( ( DEEP(root->left) - DEEP(root->right) ) == 2 )
		{
			if(val < root->left->val)
			{
				printf("do ll...val=%d\n", val);
				root = ll_rotation(root);
			}
			else
			{
				printf("do lr...val=%d\n", val);
				root = lr_rotation(root);
			}
		}
	}
	else if(val > root->val)
	{
		root->right = avltree_insert_node(root->right, val);
		
		if( ( DEEP(root->right) - DEEP(root->left) ) == 2 )
		{
			if(val > root->right->val)
			{
				printf("do rr...val=%d\n", val);
				root = rr_rotation(root);
			}
			else
			{
				printf("do rl...val=%d\n", val);
				root = rl_rotation(root);
			}
		}
	}
	else
		printf("insert %d fails, val exsits.\n", val);
	
	root->deep = MAX( DEEP(root->left), DEEP(root->right) ) + 1;
	
	return root;
}

 删除指定值得节点

/* Delete Node of AVL Tree
** Parameters:
**		root - avl tree
**		val - node's val
*/
PNode avltree_delete_node_by_val(PNode root, int val)
{
	PNode node;
	
	node = avltree_search_node(root, val);
	
	if(node)
	{
		printf("find node, val=%d\n", node->val); 
		avltree_delete_node(root, node);
	}
	else
		printf("not find val:%d\n", val);
		 
	return root;
}

 删除指定节点

/* Delete Node of AVL Tree
** Parameters:
**		root - avl tree
**		node - node to delete
*/
PNode avltree_delete_node(PNode root, PNode node)
{
	if(root == NULL || node == NULL)
		return NULL;
		
	if(node->val < root->val)	// left side
	{
		root->left = avltree_delete_node(root->left, node);
		if(DEEP(root->right) - DEEP(root->left) == 2)
		{
			PNode r = root->right;
			if(DEEP(r->left) > DEEP(r->right))
				root = rl_rotation(root);
			else
				root = rr_rotation(root);
		}
	}
	else if(node->val > root->val) // right side
	{
		root->right = avltree_delete_node(root->right, node);
		if(DEEP(root->left) - DEEP(root->right) == 2)
		{
			PNode l = root->left;
			if(DEEP(l->left) > DEEP(l->right))
				root = ll_rotation(root);
			else
				root = lr_rotation(root);
		}
	}
	else // find
	{
		if(root->left && root->right)
		{
			if(DEEP(root->left) > DEEP(root->right))
			{
				// 1. find max node in left side
				// 2. set max node val to root
				// 3. delete max node
				PNode maxNode = avltree_get_max(root->left);
				root->val = maxNode->val;
				root->left = avltree_delete_node(root->left, maxNode);
			}
			else
			{
				// 1. find min node in right side
				// 2. set min node val to root
				// 3. delete min node
				PNode minNode = avltree_get_min(root->right);
				root->val = minNode->val;
				root->right = avltree_delete_node(root->right, minNode);
			}
		}
		else
		{
			PNode tmp = root;
			root = root->left ? root->left : root->right;
			free(tmp);
		}
	}
	
	return root;
}

销毁树 

/* Destory AVL Tree
** Parameters:
**		root - avl tree
*/
void avltree_destory(PNode root)
{
	if(root == NULL)
		return;
	
	if(root->left)
		avltree_destory(root->left);
	if(root->right)
		avltree_destory(root->right);
	
	printf("free %d\n", root->val);
	free(root);
}

测试实例

int main(void)
{
	int i;
	int len;
	int nodes[] ={3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9};
	len = sizeof(nodes) / sizeof(nodes[0]);
	
	PNode root = NULL;
	for(i=0; i<len; i++)
	{
		root = avltree_insert_node(root, nodes[i]);
	}
	
	printf("\npreorder:\n");
	preorder(root);
	
	printf("\ninorder:\n");
	inorder(root);
	
	printf("\npreorder:\n");
	postorder(root);
	
	printf("\nroot's deep: %d\n", DEEP(root));
	printf("\nminimum: %d\n", avltree_get_min(root)->val);
	printf("\nmaxmum: %d\n", avltree_get_max(root)->val);

	printf("-----------delete node start-------\n");
	avltree_delete_node_by_val(root, 12);
	printf("\npreorder:\n");
	preorder(root);
	printf("\n");
	
	avltree_delete_node_by_val(root, 2);
	printf("\npreorder:\n");
	preorder(root);
	printf("\n");
	
	printf("-----------delete node end-------\n");
	
	
	printf("\ndesotry avl tree\n");
	avltree_destory(root);
	
	return 0;
}

构建完成的树:

 

 执行结果:

do ll...val=1
do rr...val=5
do rr...val=6
do rr...val=7
do rl...val=15
do rl...val=14
do rr...val=13
do ll...val=12
do ll...val=11
do ll...val=10
do lr...val=9

preorder:
val=7, deep=5
val=4, deep=3
val=2, deep=2
val=1, deep=1
val=3, deep=1
val=6, deep=2
val=5, deep=1
val=13, deep=4
val=11, deep=3
val=9, deep=2
val=8, deep=1
val=10, deep=1
val=12, deep=1
val=15, deep=2
val=14, deep=1
val=16, deep=1

inorder:
val=1, deep=1
val=2, deep=2
val=3, deep=1
val=4, deep=3
val=5, deep=1
val=6, deep=2
val=7, deep=5
val=8, deep=1
val=9, deep=2
val=10, deep=1
val=11, deep=3
val=12, deep=1
val=13, deep=4
val=14, deep=1
val=15, deep=2
val=16, deep=1

preorder:
val=1, deep=1
val=3, deep=1
val=2, deep=2
val=5, deep=1
val=6, deep=2
val=4, deep=3
val=8, deep=1
val=10, deep=1
val=9, deep=2
val=12, deep=1
val=11, deep=3
val=14, deep=1
val=16, deep=1
val=15, deep=2
val=13, deep=4
val=7, deep=5

root's deep: 5

minimum: 1

maxmum: 16
-----------delete node start-------
find node, val=12

preorder:
val=7, deep=5
val=4, deep=3
val=2, deep=2
val=1, deep=1
val=3, deep=1
val=6, deep=2
val=5, deep=1
val=13, deep=4
val=10, deep=3
val=9, deep=2
val=8, deep=1
val=11, deep=1
val=15, deep=2
val=14, deep=1
val=16, deep=1

find node, val=2

preorder:
val=7, deep=5
val=4, deep=3
val=3, deep=2
val=1, deep=1
val=6, deep=2
val=5, deep=1
val=13, deep=4
val=10, deep=3
val=9, deep=2
val=8, deep=1
val=11, deep=1
val=15, deep=2
val=14, deep=1
val=16, deep=1

-----------delete node end-------

desotry avl tree
free 1
free 3
free 5
free 6
free 4
free 8
free 9
free 11
free 10
free 14
free 16
free 15
free 13
free 7

小结

总算是描述完成(其实还没有,依据示例进行构建的过程留给用心的朋友自行画图,看看结果是否符合预期,这样才能加深 AVL 几种姿态的转变的理解),对照代码和图示应该对 AVL 树有了新的认识

早期在工作项目中有看到基于 AVL 树的实现,当时一头雾水,现在是时候重温了

更多数据结构详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值