首页 > 代码库 > 平衡二叉树(AVL树)

平衡二叉树(AVL树)

平衡二叉树:是一颗空树;或者具有以下性质的树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。

平衡二叉树的关键在于插入结点时如何保持整棵树的平衡性。

下面是不平衡发生的四种情况:

(1)平衡二叉树某一节点的左孩子的左子树上插入一个新的节点,使得该节点不再平衡。

LL型(左孩子的左子树)
由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。 即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。


/* return pointer to the new subtree root */
AVLNode *single_rotate_ll(AVLNode *node)	 //LL
{
	AVLNode *p = node->lchild;

	node->lchild = p->rchild;
	p->rchild = node;
	
	node->height = max(height(node->lchild), height(node->rchild)) + 1;
	p->height = max(height(p->lchild), height(p->rchild)) + 1;
	//p->height = max(height(p->lchild), node->height) + 1;

	return p;
}

(2) 平衡二叉树某一节点的右孩子的右子树上插入一个新的节点,使得该节点不再平衡。

RR型平衡旋转法(右孩子的右子树)

由于在A的右孩子C 的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。


AVLNode *single_rotate_rr(AVLNode *node)	 //RR
{
	AVLNode *p = node->rchild;

	node->rchild = p->lchild;
	p->lchild = node;

	node->height = max(height(node->lchild), height(node->rchild)) + 1;
	p->height = max(height(p->lchild), height(p->rchild)) + 1;
	//p->height = max(node->height, height(p->rchild)) + 1;

	return p;
}

(3)平衡二叉树某一节点的左孩子的右子树上插入一个新的节点,使得该节点不再平衡。

LR型平衡旋转法(左孩子的右子树)
由于在A的左孩子B的右子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理。


AVLNode *double_rotate_lr(AVLNode *node)	//LR
{
	node->lchild = single_rotate_rr(node->lchild);
	return single_rotate_ll(node);
}

(4) 平衡二叉树某一节点的右孩子的左子树上插入一个新的节点,使得该节点不再平衡。

RL型平衡旋转法(右孩子的左子树)
由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树的根结点D向右上旋转提升到C结点的位置,然后再把该D结点向左上旋转提升到A结点的位置。即先使之成为RR型,再按RR型处理。


AVLNode *double_rotate_rl(AVLNode *node)	//RL
{
	node->rchild = single_rotate_ll(node->rchild);
	return single_rotate_rr(node);
}
AVl树的结构体为:

typedef struct AVLNode 
{
	int data;
	int height;
	struct AVLNode *lchild, *rchild;
}AVLNode;
计算以node结点为根的树的高度:
int height(AVLNode *node)
{
	if (node == NULL)
		return -1;
	else
		return node->height;
}
现在可以写出avl树的插入代码了:

AVLNode *insert(AVLNode *root, int data)
{
	if (root == NULL)
	{
		root = (AVLNode*)malloc(sizeof(struct AVLNode));
		root->data = http://www.mamicode.com/data;>


中序遍历avl树:

void inorder_traversal_tree(AVLNode *node)
{
	if (node != NULL)
	{
		inorder_traversal_tree(node->lchild);
		printf("%d ", node->data);
		inorder_traversal_tree(node->rchild);
	}
}


后续遍历以释放树的结点:

void destory_tree(AVLNode*node)
{
	if (node != NULL)
	{
		destory_tree(node->lchild);
		destory_tree(node->rchild);
		free(node), node = NULL;
	}
}


测试代码:

int main()
{
	int arr[] = {3, 2, 1, 4, 5, 6, 7, 16, 15, 14, 13, 12, 11, 10, 8, 9};
	AVLNode* T = NULL;
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
		T = insert(T, arr[i]);

	inorder_traversal_tree(T);
	destory_tree(T);
	getchar();
	return 0;
}

参考:AVL树的旋转

平衡二叉树 AVL 的插入节点后旋转方法分析