数据结构:AVL树(平衡二叉树)

一,概述

对于普通的查找二叉树,由于删除操作的某些规定,导致最终删除的结果是根节点的左右子树严重不平衡,这对他的正常操作带来严重的影响。因此提出了avl树,在avl树中,任意两个节点的子树的高度的相差最多为1。当然这对他的删除和插入操作提出了新的要求。本文将详细介绍avl树的设计思想和基本操作。

二基本术语。

有4种以下的情况可能导致二叉树不平衡,分别为:

(1)LL:插入一个新节点到根节点的左儿子的左子树,导致跟节点失去平衡。

(2)RR:插入一个新节点到根节点的右儿子的右子树,导致跟节点失去平衡。

(3)LR:插入一个新节点到根节点的左儿子的右子树,导致跟节点失去平衡。

(4)RL:插入一个新节点到根节点的右儿子的左子树,导致跟节点失去平衡。

针对上面的出现的不平衡,可以通过旋转使之恢复平衡。

(1),(2)种情况,使用单旋转。

(3),(4)种情况,使用双旋转。

三,详细介绍上面的四种情况,以及解决方法。

情况1:对于比较简单的a情形:


3为插入的值,相应的代码(单旋转)如下,不考虑高度的情况下

//设5这个节点为k2,即传入的参数为k2(因为这个节点失去平衡)
position singlerotatewithleft(position k2){
	position k1;
	k1=k2->left;
	k1->right =k2;
	return k1;
}
对于较复杂的情形b呢?如下图所示:




4为插入的值,相应的代码(单旋转)如下,不考虑高度的情况下

//设8这个节点为k2,即传入的参数为k2(因为这个节点失去平衡)
position singlerotatewithleft(position k2){
	position k1;
	k1=k2->left;
	k2->left=k1->right;
	k1->right =k2;
	return k1;
}
发现没有,仅仅比上面多了一行代码,而这行代码在情形a中也适合。
那么对于更复杂的情形c呢,如下图所示,


初看,5,7两个节点都是不平衡的节点,那么我们应该平衡5,再平衡7节点。但是事实上,只要解决了5节点,那么7节点就不需要解决了。(存不存在既要解决两个节点的情况呢,当然存在,当6有右孩子的时候,仍然要平衡7,所以要递归遍历),对于这段代码,同上面。

情况2与1相似,这里不做讨论,给出以下代码:

position singlerotatewithright(position k2){
	position k1;
	k1=k2->right;
	k2->right=k1->left;
	k1->left =k2;
	return k1;
}

情况3:解决方法是对失衡节点的左儿子进行右单旋转,然后再对它本身进行左单旋转。(情况4相反)

如下图:


看了上图,是不是对定义 LR:插入一个新节点到根节点的左儿子的右子树,导致跟节点失去平衡,更清楚了呢?为什么是左儿子,为什么是右子树。

那么他的实现代码如下所示:

position doublerotatewithleft(position k2){
	k2->left = singlerotatewithright(k2->left);
	return singlerotatewithleft(k2);
}
四,关于删除操作。

暂时不考虑。。。。

总的实现代码如下所示:

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

typedef struct treenode{
	struct treenode *left;
	struct treenode *right;
	int height; //高度:叶子节点高度为0
	int element;
}treenode;

typedef treenode* position;
position single_rotate_left(position );
int height(position);
position insert_node(position,int);
position double_rotate_leftright(position);
position double_rotate_rightleft(position);
int max_2(int x,int y){
	if(x>y){
		return x;
	}
	return y;
}
int height(position p1){
	if(p1==NULL){
		//为什么是-1呢?因为叶子节点是0,而基本的操作是节点的高度为
		//节点的左子树与右子树的最大值加1,所以必须是-1
		return -1;
	}
	return p1->height;
}

position single_rotate_left(position k2){
	position k1;
	
	k1 = k2->left;
	k2->left=k1->right;
	k1->right=k2;
	
	//因为除了k2和k1节点的高度变了,而其他的节点的高度都没有变
	//那么可以用最简单的方法求出他们的高度。。。
	
	k1->height=max_2(height(k1->left),height(k1->right))+1;
	k2->height=max_2(height(k2->left),height(k2->right))+1;
	return k1;
}

position single_rotate_right(position k2){
	position k1;
	
	k1=k2->right;
	k2->right=k1->left;
	k1->left =k2;

	k1->height=max_2(height(k1->left),height(k1->right))+1;
	k2->height=max_2(height(k2->left),height(k2->right))+1;
	
	return k1;
}
position double_rotate_leftright(position p){
	p->left = single_rotate_right(p->left);
	p=single_rotate_left(p);
	return p;
}
position double_rotate_rightleft(position p){
	p->right=single_rotate_left(p->right);
	p=single_rotate_right(p);
	return p;
}

//其实插入操作整个过程和查找二叉树的方法类似
position insert_node(position t,int x){
	if(t==NULL){
		
	//可以插入一个新的节点
		position p =(position)malloc(sizeof(treenode));
		p->height=0;
		p->element = x;
		p->left=NULL;
		p->right=NULL;
		t=p;
	}else{
		if(x<t->element){
			t->left=insert_node(t->left,x);
			//对于下面这个判断,应该非常清楚
			//左右子树相减为2,说明出现不平衡
			if(height(t->left)-height(t->right)==2){
				//这判断的依据来源于:
				//是插入左儿子的左子树中,还是插入到做儿子的右子树中
				//因此 只要比较左儿子的 值就可以了
				if(x<t->left->element){
					t=single_rotate_left(t);
				}else if(x>t->left->element){
					t=double_rotate_leftright(t);
				}
			}
		}
		else if(x>t->element){
			t->right=insert_node(t->right,x);

			if(height(t->right)-height(t->left)==2){
				if(x>t->right->element){
					t=single_rotate_right(t);
				}else if(x<t->right->element){
					t=double_rotate_rightleft(t);
				}
			}
			
		}
	}
	
	t->height=max_2(height(t->left),height(t->right)) + 1;
	
	return t;
}
position initial(){
	position p=(position)malloc(sizeof(treenode));
	p->height=0;
	p->left=NULL;
	p->right=NULL;
	return p;
}
void tree_print(position p){
	if(p==NULL)
	{
		return;
	}
	tree_print(p->left);
	printf(" %d ",p->element);
	tree_print(p->right);
}
void main(){
	position p=initial();
	p->element= 5;
	insert_node(p,2);
	insert_node(p,6);
	insert_node(p,1);
	insert_node(p,4);
	//为什么是p=insert_node(p,3);而前面不需要,因为根节点在这个时候产生变化
	p=insert_node(p,3);
	tree_print(p);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值