1. 程式人生 > >【資料結構】AVL樹詳解

【資料結構】AVL樹詳解

1.什麼是AVL樹

AVL樹又稱平衡二叉搜尋樹,它能保證二叉樹高度相對平衡,儘量降低二叉樹的高度,提高搜尋效率。單純的二叉搜尋樹在最壞的情況下插入查詢刪除等操作時間複雜度會是O(N), 例如:
所以,AVL樹就能避免這種情況,使得增刪查改的時間複雜度為O(lgN). (ps:這裡的lgN指的是log以2為底的N。)那麼AVL樹是怎麼做到的呢,看看它的特點你就大概知道了: 1> 左子樹和右子樹的高度之差的絕對值不超過1  2.>樹中的每個左子樹和右子樹都是AVL樹 3.>每個節點都有一個平衡因子(balance factor--bf),任一節點的平衡因子是-1,0,1。(每個節點的平衡因子等於右子樹的高度減去左子 樹的高度 ) . 思考這樣一個問題,既然保證了二叉樹的高度之差不超過1就能降低時間複雜度,那直接保證左右子樹高度相同豈不是更好?當然是不行的,假如這棵樹只有兩個節點,那要怎麼辦呢?所以,我們只能保證高度差不能超過1 。

2.如何構建AVL樹

    按照普通的插入,如果給一個數組是:1,2,3,4,5,6,7 ,一定會導致上面圖的那種情況,所以,我們要進行調整。那麼什麼情況下需要調整,怎麼調呢? 需要調整的情況大概分為以下幾種:   1>需要右單旋:      圖片是一種直觀的表現,過程是這個樣子的:     先將p節點的右樹(如果存在的話)當做g的左樹,斷開了p,g的原始關係,再讓p的右指向g,建立新的聯絡。這裡注意,因為我的AVL樹時三叉鏈結構(左,右,父)在讓g的父親節點指向p之前需要先儲存g的父親節點。因為,如果g的父親存在的話,最後要讓它指向p(如上圖)。旋轉沒什麼難的,但是要注意平衡因子的調整。然後就是要細心,一定要注意三叉鏈在改變指標指向的時候也要改變父親指標的指向!博主就這裡不知道自己當時哪裡抽了好幾處沒有改變父親節點的指向,雖然這個錯誤很容易找到,但是還是要花費我寶貴的時間! 2>需要左單旋:

右單旋與左單旋類似,就是節點的名稱我改變了一下,方便大家理解程式碼。 3>需要左右雙旋
    可以看到,左右雙旋其實是先以p為軸左旋一次,再以g為軸右旋一次。但實際上如果左右旋只調用兩個單旋是會出現問題的。沒錯,問題就出在平衡因子上。按照上面說單旋時的結點位置變化情況,我們可以畫一張圖來表示右左單旋最終結點的變化情況。
4>需要右左雙旋: 與左右雙旋類似,右左雙旋也是先右旋後左旋,對平衡因子的處理也跟左右雙旋很類似,根據它結點最終的位置確定平衡因子。

3.如何插入

1>如果根節點為空,直接插入賦給根節點 2>通過關鍵碼先找到插入的位置,如果找到了與給定關鍵碼相同的碼,則插入失敗(key結構和key+value結構都不允許樹中出現重複關鍵碼,會導致查找出錯) 3>插入,插到左邊平衡因子減一,插到右邊加一 4>檢查是否平衡,不平衡則調節至平衡(具體程式碼裡有註釋做了相關解釋)

4.檢查AVL樹是否平衡

   思想:遞迴算出每個結點的左右樹高度,用右樹減左樹的數值和結點的平衡因子比較,如果相等,則該節點平衡,就這樣遞迴去判斷。 上述問題的程式碼實現:
#include<iostream>

using namespace std;

template<class K,class V>
struct AVLtreeNode    //結點結構體
{
	typedef AVLtreeNode<K, V> Node;
	Node* _left;
	Node* _right;
	Node* _parent;

	K _key;
	V _value;
	int _bf;
	AVLtreeNode(const K& key,const V& value)
		:_key(key)
		, _value(value)
		, _left(NULL)
		, _right(NULL)
		, _parent(NULL)
		, _bf(0)    //平衡因子
	{}
};

template<class K,class V>
class AVLtree
{
	typedef AVLtreeNode<K, V> Node;
public:
	AVLtree()
		:_root(NULL)
	{}

	bool Insert(const K& key, const V& value)
	{
		if (NULL == _root)
		{
			_root = new Node(key, value);
			return true;
		}
		Node* cur = _root;
		Node* parent = NULL;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key>key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}

		}
		if (parent->_key < key)
		{
			Node* tmp = new Node(key, value);
			parent->_right = tmp;
			tmp->_parent = parent;
			parent->_bf++;
		}
		if (parent->_key>key)
		{
			Node* tmp = new Node(key, value);
			parent->_left = tmp;
			tmp->_parent = parent;
			parent->_bf--;
		}
		while (parent)
		{
			if (parent->_bf == 0)  //原來是1或者-1,那插入以後不會對樹的高度有影響;
			{
				return true;
			}
			else if (parent->_bf == 1 || parent->_bf == -1) //原來是0,對樹的高度有影響
			{
				Node* pparent = parent->_parent;
				if (pparent != NULL)
				{
					if (pparent->_left == parent)// 左樹上高度增加
						pparent->_bf--;
					else //右樹上高度增加
						pparent->_bf++;
				}

				parent = pparent;
			}
			else //平衡因子是2/-2,從1或者-1變過來,不滿足平衡樹,需要旋轉
			{
				if (parent->_bf == 2)  //右樹過高
				{
					if (parent->_right->_bf == 1) //需要左旋結構
					{
						_RotateL(parent);
						return true;
					}
					else if (parent->_right->_bf = -1)  //需要右左旋結構
					{
						_RotateRL(parent);
						return true;
					}
				}
				else //平衡因子為-2
				{
					if (parent->_left->_bf == -1)  //需要右單旋的結構
					{
						_RotateR(parent);
						return true;
					}
					else if (parent->_left->_bf == 1) //需要左右旋的結構
					{
						_RotateLR(parent);
						return true;
					}
				}
			}
		}
	}
	

	bool IsBalance()
	{
		return _IsBalance(_root);
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

protected:
	bool _IsBalance(Node* root)
	{
		if (NULL == root)
			return true;
		int bf = _Depth(root->_right) - _Depth(root->_left);
		if (bf == root->_bf)
			return true;
		else
		{
			cout << root->_key << "平衡因子異常" << endl;
			return false;
		}
			
		return _IsBalance(root->_left);
		return _IsBalance(root->_right);
	}

	int _Depth(Node* root)
	{
		if (NULL == root)
			return 0;
		int left = _Depth(root->_left)+1;
		int right = _Depth(root->_right) + 1;
		return left > right ? left : right;
	}


	void _InOrder(Node* root)
	{
		if (NULL == root)
			return;
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);

	}
	void _RotateL(Node* parent)   //左單旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		Node* pparent = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (NULL == pparent)
		{
			_root = subR;
			subR->_parent = NULL;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subR;
				subR->_parent = pparent;
			}
			else
			{
				pparent->_right = subR;
				subR->_parent = pparent;
			}
		}
		parent->_bf = subR->_bf = 0;
	}

	void _RotateR(Node* parent)    //右單旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		Node* pparent = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

		if (pparent == NULL)
		{
			_root = subL;
			subL->_parent = NULL;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subL;
				subL->_parent = pparent;
			}
			else
			{
				pparent->_right = subL;
				subL->_parent = pparent;
			}			
		}
		parent->_bf = subL->_bf = 0;
	}

	void _RotateRL(Node* parent)    //右左雙旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;  //必須記錄下來,旋轉後發生改變
		_RotateR(subR);
		_RotateL(parent);

		if (bf == 0)    //插入後子樹根節點為0,高度沒有受到影響,則調整後父節點和祖父結點的平衡因子為0
		{
			parent->_bf = subRL->_bf=subR->_bf = 0;
		}
		else if (bf == 1)  //插入後右樹高,右樹給了subR的左邊,所以subR的平衡因子為0,左樹高度低於右樹,給了parent的右樹所以parent的平衡因子為1
		{
			parent->_bf = -1;
			subR->_bf = subRL->_bf = 0;
		}
		else if (bf == -1)//插入後左樹高,右樹給了subR的左邊,所以subR的平衡因子為1,左樹高度低於右樹,給了parent的右樹所以parent的平衡因子為0
		{
			parent->_bf = subRL->_bf = 0;
			subR->_bf = 1;
		}
	}

	void _RotateLR(Node* parent)   //左右雙旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;
		_RotateL(subL);
		_RotateR(parent);

		if (bf == 0)
			parent->_bf =subLR->_bf= subL->_bf = 0;
		else if (bf == 1)
		{
			parent->_bf = subLR->_bf = 0;
			subL->_bf = -1;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			subL->_bf = subLR->_bf = 0;
		}
	}
private:
	Node* _root;
};

void TestAVLtree()
{
	AVLtree<int, int> tree;
	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		tree.Insert(a[i], i);
		cout << "isbalance?"<<tree.IsBalance() <<"插入"<< a[i] << endl;
	}
	tree.InOrder();
	tree.IsBalance();
	AVLtree<int, int> tree1;
	int a1[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	for (size_t i = 0; i < sizeof(a1) / sizeof(a1[0]); i++)
	{
		tree1.Insert(a1[i], i);
		cout << "isbalance?" << tree1.IsBalance() << "插入" << a1[i] << endl;
	}
	tree1.InOrder();
}


如果有沒寫清楚或者寫錯了的地方請多指教