AVL樹學習

1.什麼是AVL樹

          AVL樹是帶有平衡條件的二叉查找樹,是其每個結點的左子樹和右子樹的高度差最多差1的二叉查找樹。如圖1,左邊的樹是AVL樹,但右邊的不是。


圖1 左邊的是AVL樹,右邊的不是


2.AVL樹的基本操作

         當向一棵AVL樹中插入一個結點時,可能破壞AVL樹的特性(例如,將6插入圖1的AVL樹中將會破壞項爲8的結點平衡條件)。因此,我們需要對樹進行簡單的修正來恢復AVL樹的平衡,這種修正操作叫做旋轉(rotation)。在插入以後,只有那些從插入點到根節點的路徑上的結點的平衡可能被破壞,因爲只有這些結點的子樹可能發生變化,在這條路徑上可以發現一個結點,它的新平衡破壞了AVL條件,可以證明,恢復這個結點的平衡就可以保證整個樹的AVL性質。

         我們把必須平衡的結點叫做a。則AVl樹的不平衡可能是由以下四種情形造成的:

         (1)對a的左兒子的左子樹進行一次插入

         (2)對a的左兒子的右子樹進行一次插入

         (3)對a的右兒子的左子樹進行一次插入

         (4)對a的右兒子的右子樹進行一次插入

         對於(1)和(4),可以通過單旋轉完成AVL樹的修正,對於(2)和(3),可以通過雙旋轉來完成AVL樹的修正。以下詳細介紹單旋轉和雙旋轉。

1.1單旋轉

         圖2顯示了向右單旋轉如何調整情形(1),其中k2表示插入後不平衡結點a。


圖2 向右單旋轉修正情形(1)

          圖3顯示了在將6插入左邊原始的AVL樹後結點8便不再平衡,於是,我們在7和8之間做一次單旋轉,結果得到右邊的樹。


圖3 插入6破壞了AVL性質,而後經過單旋轉又將AVL性質恢復

           圖4說明了向左單旋轉如何調整情形(4),與情形(1)對稱。


圖4 向左單旋轉修正情形(4)

         圖5中,左邊的圖是在原來的AVL樹中加入結點7,結點5不再平衡,破壞了AVL特性。右邊的圖是經過單旋轉修正之後的樹。


圖5 插入7破壞了AVL性質,而後經過單旋轉又將AVL性質恢復

1.2雙旋轉

         圖6說明了如何通過左-右雙旋轉修正情形(2),先將以k1爲根的子樹向左單旋轉,再將已k3爲根的子樹向右單旋轉,可以得到右邊的樹。


圖6 通過左-右雙旋轉修正情形(2)

         圖7說明了如何通過右-左雙旋轉修正情形(4),先將以k3爲根的子樹向右單旋轉,再將已k1爲根的子樹向左單旋轉,可以得到右邊的樹。


圖7 通過右-左雙旋轉修正情形(4)

          圖8中,左邊的圖中,在原來的AVL樹中加入結點14,結點6失去平衡,屬於情形(4),右邊的圖式經過右-左雙旋轉修正之後的樹。


圖8 插入結點14,經過右-左雙旋轉之後的樹

3.代碼實現

           AVL樹定義如下:

typedef int ElemType;

struct AvlNode
{
	ElemType elem;
	struct AvlNode *left;
	struct AvlNode *right;
	int height;   //樹節點高度
};

typedef struct AvlNode * AvlTree;//AvlTree
            AVL樹中插入操作如下:

AvlTree insert(AvlTree tree, ElemType value)
{
	if(!tree)//添加新結點
	{
		tree = (AvlTree)malloc(sizeof(AvlNode));
		if(!tree)
			exit(EXIT_FAILURE);
		tree->elem = value;
		tree->left = tree->right = NULL;
		tree->height = 0;
	}
	else if(value < tree->elem)//在左子樹中添加元素
	{
		tree->left = insert(tree->left, value);//遞歸插入,類似於二叉查找樹
		if(getHeight(tree->left) - getHeight(tree->right) == 2)//失去平衡
			if(value < tree->left->elem)		//情形(1),向右單旋轉
				tree = singleRotateWithLeft(tree);
			else					//情形(2),右-左雙旋轉
				tree = doubleRotateWithLeft(tree);
	}
	else if(value > tree->elem)//在右子樹中添加元素
	{
		tree->right = insert(tree->right, value);
		if(getHeight(tree->right) - getHeight(tree->left) == 2)//失去平衡
			if(value > tree->right->elem)		//情形(4),向左單旋轉
				tree = singleRotateWithRight(tree);
			else					//情形(3),左-右雙旋轉
				tree = doubleRotateWithRight(tree);
	}
	else//數據元素重複,什麼也不做
		;
	tree->height = max(getHeight(tree->left), getHeight(tree->right)) + 1;
	return tree;
}
        向右單旋轉代碼如下:
AvlTree singleRotateWithLeft(AvlTree k2)
{
	AvlTree k1;

	k1 = k2->left;
	k2->left = k1->right;
	k1->right = k2;

	k2->height = max(getHeight(k2->left), getHeight(k2->right)) + 1;
	k1->height = max(getHeight(k1->left), k2->height) + 1;

	return k1;
}

         向左單旋轉代碼如下:

AvlTree singleRotateWithRight(AvlTree k1)
{
	AvlTree k2;

	k2 = k1->right;
	k1->right = k2->left;
	k2->left = k1;

	k1->height = max(getHeight(k1->left), getHeight(k1->right)) + 1;
	k2->height = max(k1->height, getHeight(k2->right)) + 1;
	return k2;
}

          左-右雙旋轉代碼如下:

AvlTree doubleRotateWithLeft(AvlTree k3)
{
	singleRotateWithRight(k3->left);
	return singleRotateWithLeft(k3);
}

          右-左雙旋轉代碼如下:

AvlTree doubleRotateWithLeft(AvlTree k1)
{
	singleRotateWithLeft(k1->right);
	return singleRotateWithRight(k1);
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章