1. 程式人生 > 其它 >AVL樹的建立解析含程式碼(C++)

AVL樹的建立解析含程式碼(C++)

AVL樹

AVL樹是平衡二叉樹,它可以儘可能建立“枝繁葉茂”的樹,防止樹枝過長過少。二叉樹搜尋中,會引入ASL平均查詢長度的概念,表示查詢所有節點的比較次數平均值。平衡二叉樹的ASL相較而言較小。

思路

AVL樹就是比較各個節點左右子樹的深度差,當差值等於2時就需要對樹結構進行改變。改變的方式有左旋、右旋、左右旋以及右左旋。判斷旋轉方式是看導致深度差值的結點在哪。左旋是順時針旋轉相關結點,右旋為逆時針旋轉相關結點。左右旋及右左旋為左旋、右旋的組合,左右旋先右旋後左旋。
如下面圖的示例:



以上圖片來自陳越《資料結構》一書。
關於旋轉結點的程式碼不是很難,RL旋和LR旋為各是兩個單旋的組合,L旋和R旋的思路又都一樣。因此只要搞懂一個方向的單旋即可。注意單旋時旋轉結點的子結點位置的改變。具體的看程式碼。

程式碼

#include <iostream>
using namespace std;

/*avl平衡樹的建立,需要判斷一個節點左右兩子樹的深度差,差值小於2視為平衡*/
typedef struct node{
	int data;
	node* lchild;
	node* rchild;
	int height;
}*bitTree;

int max(int a,int b)
{//用於比較子樹大小 
	return a>b?a:b;	
} 
int getHeight(bitTree T)
{//獲取樹的高度 
	int lnum,rnum,count;
	if(T){
		lnum = getHeight(T->lchild);
		rnum = getHeight(T->rchild);
		count = lnum>rnum?lnum:rnum;//比較左右子樹的高度取最大值 
		return (count+1);
	}	
}
bitTree singleleftRotation(bitTree T)//左旋 
{
	bitTree B = new node;
	B = T->lchild;
	T->lchild = B->rchild;
	B->rchild = T;
	T->height = max(getHeight(T->lchild),getHeight(T->rchild))+1;
	B->height = max(getHeight(B->lchild),T->height)+1;
	return B;
}
bitTree singlerightRotation(bitTree T)
{
	bitTree B = T->rchild;
	T->rchild = B->lchild;
	B->lchild = T;
	T->height = max(getHeight(T->lchild),getHeight(T->rchild))+1;
	B->height = max(getHeight(B->rchild),T->height)+1;
}
bitTree doubleleftrightRotation(bitTree T)
{
	T->lchild = singlerightRotation(T->lchild);
	return singleleftRotation(T);
}
bitTree doublerightleftRotation(bitTree T)
{
	T->rchild = singleleftRotation(T->lchild);
	return singlerightRotation(T);
}
bitTree insert(bitTree &T,int x)
{//AVL樹在插入的過程中不斷判斷
	if(!T)//插入一個空樹 
	{
		T = new node;
		T->data = x;
		T->lchild = T->rchild = NULL;
		T->height = 1;//這裡樹高的初值不重要,後續的高度都會做左右子樹高度對比重新取值
	}
	else if(x<T->data){//插入值在左子樹中 
		T->lchild = insert(T->lchild,x);
		//對旋的判斷就是看其插入的位置 
		if(getHeight(T->lchild)-getHeight(T->rchild)==2){
			if(x<T->lchild->data)//需要L旋 
				T = singleleftRotation(T);
			else//左右旋 
				T = doubleleftrightRotation(T);
		}
	}
	else if(x>T->data){//插入值在右子樹中 
		T->rchild = insert(T->rchild,x);
		//對旋的判斷就是看其插入的位置 
		if(getHeight(T->lchild)-getHeight(T->rchild)==2){
			if(x>T->rchild->data)//需要R旋 
				T = singlerightRotation(T);
			else//RL旋 
				T = doublerightleftRotation(T);
		}
	}
	T->height = max(getHeight(T->lchild),getHeight(T->rchild))+1;//插入了新值,樹的高度加一 
	return T; 
}
int display(bitTree T)
{//先序遍歷
	if(T!=NULL)
	{
		display(T->lchild);
		cout<<T->data<<" ";
		display(T->rchild);
	}
}
int main()
{
/*
   示例生成二叉樹:
	            18
                   /   \
		 10     20
	       /   \   /   \
	      9    11 19   22
*/ 
	bitTree T;
	int a[7] = {9,11,19,22,18,10,20};
	for(int i = 0;i<7;i++)
		insert(T,a[i]);
	display(T);
//輸出9 10 11 18 19 20 22
}

注意點

結點的單旋需要傳入的結點為第一個深度不平衡的結點。多旋則是先對不平衡結點的下一個子節點單旋,後對不平衡結點再做一次單旋。
下面以RL旋做一個例子