1. 程式人生 > >資料結構——AVL樹

資料結構——AVL樹

    AVL樹是最早提出的自平衡二叉樹,在AVL樹中任何節點的兩個子樹的高度最大差別為一,即|HL-HR|<=1,所以它也被稱為高度平衡樹。AVL樹得名於它的發明者G.M. Adelson-VelskyE.M. Landis,他們在1962年的論文《An algorithm for the organization of information》中發表了它。

       AVL樹中查詢、插入和刪除在平均和最壞情況下都是O(log n),增加和刪除可能需要通過一次或多次樹旋轉來重新平衡這個樹。確定並刪除識別符號為x的元素、及確定並刪除第k個最小元素都可以在O(log n)時間內完成。

       有四種插入方式會打破樹的平衡:

(1)LL:插入一個新節點到根節點的左子樹(Left)的左子樹(Left),導致根節點的平衡因子由1變為2

(2)RR:插入一個新節點到根節點的右子樹(Right)的右子樹(Right),導致根節點的平衡因子由-1變為-2

(3)LR:插入一個新節點到根節點的左子樹(Left)的右子樹(Right),導致根節點的平衡因子由1變為2

(4)RL:插入一個新節點到根節點的右子樹(Right)的左子樹(Left),導致根節點的平衡因子由-1變為-2

 針對四種種情況可能導致的不平衡,可以通過旋轉使之變平衡。有兩種基本的旋轉:

(1)左旋轉:將根節點旋轉到(根節點的)右孩子的左孩子位置

(2)右旋轉:將根節點旋轉到(根節點的)左孩子的右孩子位置

注意:旋轉需要保證的是該數的中序遍歷節點的順序不變!

基本的資料結構:

typedef struct BSTNode
{
	int data;
	int bf; //結點的平衡因子
	struct BSTNode *lchild,*rchild;//左、右孩子指標
}BSTNode,*BSTree;
右旋轉:



程式碼如下:

//對以*p為根的二叉排序樹作右旋處理,LL型平衡旋轉法
void R_Rotate(BSTree &p)
{
	BSTree lc;
	lc = p->lchild; //lc指向的*p左子樹根結點
	p->lchild = lc->rchild; //lc的右子樹掛接為*p的左子樹
	lc->rchild = p;
	p = lc; //p指向新的結點
}
左旋轉:


程式碼如下:

//對以*p為根的二叉排序樹作左旋處理,RR型平衡旋轉法
void L_Rotate(BSTree &p)
{
	BSTree rc;
	rc = p->rchild; //rc指向的*p右子樹根結點
	p->rchild = rc->lchild; //rc的左子樹掛接為*p的右子樹
	rc->lchild = p;
	p = rc; //p指向新的結點
}
先左旋、後右旋:


程式碼如下:

//對以指標T所指結點為根的二叉樹作左平衡旋轉處理,LR型平衡旋轉法
void LeftBalance(BSTree &T)
{
	BSTree lc,rd;
	lc = T->lchild; //lc指向*T的左子樹根結點
	switch(lc->bf) //檢查*T的左子樹的平衡度,並作相應平衡處理
	{
	case LH: //新結點插入在*T的左孩子的左子樹上,要作單右旋處理
		T->bf = lc->bf = EH;
		R_Rotate(T); break;
	case RH: //新結點插入在*T的左孩子的右子樹上,要作雙旋處理
		rd = lc->rchild; //rd指向*T的左孩子的右子樹根
		switch(rd->bf) //修改*T及其左孩子的平衡因子
		{
		case LH:T->bf = RH; lc->bf = EH; break;
		case EH:T->bf = lc->bf = EH; break;
		case RH:T->bf = EH; lc->bf = LH; break;
		}
		rd->bf = EH;
		L_Rotate(T->lchild); //對*T的左子樹作左旋平衡處理
		R_Rotate(T); //對*T作右旋平衡處理
	}
}
先右旋轉後左旋轉同上。


插入操作:就是在不同情況下采用旋轉方式調整AVL樹的平衡

程式碼如下:

//插入結點e,若T中不存在和e相同關鍵字的結點,則插入一個數據元素為e的新結點,並返回1,否則返回0
bool InsertAVL(BSTree &T,int e,bool &taller)
{
	if(!T)//插入新結點,樹"長高",置taller為true
	{
		T = (BSTree)malloc(sizeof(BSTNode));
		T->data = e;
		T->lchild = T->rchild =NULL;
		T->bf = EH; taller = true;
	}
	else
	{
		if(EQ(e,T->data)) //樹中已存在和有相同關鍵字的結點則不再插入
		{
			taller = false;
			printf("The node have already exist!\n");
			return 0;
		}
		if(LT(e,T->data)) //應繼續在*T的左子樹中進行搜尋
		{
			if(!InsertAVL(T->lchild,e,taller))
				return 0;//未插入
			if(taller) //已插入到*T的左子樹中且左子樹"長高"
			{
				switch(T->bf) //檢查*T的平衡度
				{
				case LH: //原本左子樹比右子樹高,需要作左平衡處理
					LeftBalance(T);
					taller = false; break;
				case EH: //原本左子樹、右子等高,現因左子樹增高而使樹增高
					T->bf = LH;
					taller = true; break;
				case RH: //原本右子樹比左子樹高,現左、右子樹等高
					T->bf = EH;
					taller = false; break;
				}
			}
		}
		else //應繼續在*T的右子樹中進行搜尋
		{
			if(!InsertAVL(T->rchild,e,taller))
				return 0;//未插入
			if(taller) //已插入到*T的右子樹中且右子樹"長高"
			{
				switch(T->bf) //檢查*T的平衡度
				{
				case LH: //原本左子樹比右子樹高,現左、右子樹等高
					T->bf = EH; taller = false; break;
				case EH: //原本左子樹、右子等高,現因右子樹增高而使樹增高
					T->bf = RH; taller = true; break;
				case RH: //原本右子樹比左子樹高,需要作右平衡處理
					RightBalance(T); taller = false; break;
				}
			}
		}
	}
	return 1;
}//InsertAVL
刪除操作:首先定位要刪除的節點,然後用該節點的右孩子的最左孩子替換該節點,並重新調整以該節點為根的子樹為AVL樹,具體調整方法跟插入資料類似。 程式碼如下: 刪除節點時左平衡旋轉操作處理:
//刪除結點時左平衡旋轉處理
void LeftBalance_div(BSTree &p,int &shorter)
{
	BSTree p1,p2;
	if(p->bf==1) //p結點的左子樹高,刪除結點後p的bf減1,樹變矮
	{
		p->bf=0; shorter=1;
	}
	else if(p->bf==0)//p結點左、右子樹等高,刪除結點後p的bf減1,樹高不變
	{
		p->bf=-1; shorter=0;
	}
	else //p結點的右子樹高
	{
		p1=p->rchild;//p1指向p的右子樹
		if(p1->bf==0)//p1結點左、右子樹等高,刪除結點後p的bf為-2,進行左旋處理,樹高不變
		{
			L_Rotate(p);
			p1->bf=1;
			p->bf=-1;
			shorter=0;
		}
		else if(p1->bf==-1)//p1的右子樹高,左旋處理後,樹變矮
		{
			L_Rotate(p);
			p1->bf=p->bf=0; shorter=1;
		}
		else //p1的左子樹高,進行雙旋處理(先右旋後左旋),樹變矮
		{
			p2=p1->lchild;
			p1->lchild=p2->rchild; p2->rchild=p1; p->rchild=p2->lchild; p2->lchild=p;
			if(p2->bf==0)
			{
				p->bf=0; p1->bf=0;
			}
			else if(p2->bf==-1)
			{
				p->bf=1;p1->bf=0;
			}
			else
			{
				p->bf=0;
				p1->bf=-1;
			}
			p2->bf=0;
			p=p2;
			shorter=1;
		}
	}
}
刪除節點時右旋轉操作處理:
//刪除結點時右平衡旋轉處理
void RightBalance_div(BSTree &p,int &shorter)
{
	BSTree p1,p2;
	if(p->bf==-1)
	{
		p->bf=0; shorter=1;
	}
	else if(p->bf==0)
	{
		p->bf=1; shorter=0;
	}
	else
	{
		p1=p->lchild;
		if(p1->bf==0)
		{
			R_Rotate(p);
			p1->bf=-1; p->bf=1; shorter=0;
		}
		else if(p1->bf==1)
		{
			R_Rotate(p);
			p1->bf=p->bf=0; shorter=1;
		}
		else
		{
			p2=p1->rchild;
			p1->rchild=p2->lchild; p2->lchild=p1; p->lchild=p2->rchild; p2->rchild=p;
			if(p2->bf==0)
			{
				p->bf=0;
				p1->bf=0;
			}
			else if(p2->bf==1)
			{
				p->bf=-1;
				p1->bf=0;
			}
			else
			{
				p->bf=0; p1->bf=1;
			}
			p2->bf=0; p=p2; shorter=1;
		}
	}
}