天天看點

資料結構之平衡二叉樹AVL

平衡二次樹AVL:是二叉查找樹的改進版,不會像二叉查找樹一樣,在碰到序列有序是糟糕成達到O(n),平很二叉樹在每次插入的時候,都會試圖檢查左右子樹的高度差是不超過1(包括1),如果超過了,就會調整樹的結構,使樹一直保持平衡,以至于達到非常好的效率,O(logn).

#include <iostream>
using namespace std;
typedef struct BBNode
{
	int m_data;
	// 平衡因子,如果【左子樹的深度】減去【右子樹的深度】,右因為平衡二叉樹左右子樹的深度查不超過1(包括一),是以factor的值為【-1, 0, 1】
	int m_factor;
	BBNode* m_lchild;
	BBNode* m_rchild;
}BBNode, *BBSTree;
           
/*
         a             b
        /             / \
       b      =>     c   a
      /
     c
*/
// 向右旋轉
void R_Rotate(BBSTree& tree)
{
 // 左孩子
 BBNode* lchild = tree->m_lchild;
 // 【左孩子】已經有指針指向了,是以可以指向别的地方了,那麼就指向根【左孩子】的【右孩子】
 tree->m_lchild = lchild->m_rchild;
 // 【左孩子】的【右孩子】已經有指針指向了,那麼可以指向根結點了
 lchild->m_rchild = tree;
 tree = lchild;
}
           
/*
	a		       b
	 \		      / \
	  b	   =>    a   c
	   \
	    c
*/
// 向左旋轉
void L_Rotate(BBSTree& tree)
{
	BBNode* rchild = tree->m_rchild;
	tree->m_rchild = rchild->m_lchild;
	rchild->m_lchild = tree;
	tree = rchild;
}
           
/*
	 a		      a			    c
	/		     /			   / \
   b	=>      c	     =>	    b   a
	\		   /
	 c		  b
*/
// 調整左子樹的平衡
// 1:單旋轉-右旋轉
// 2: 雙旋轉-【左子樹】先左旋轉-再右旋轉
void LeftBalance(BBSTree& tree)
{
	BBNode* lchild = tree->m_lchild;
	// 如果是偏向一邊的(根->左孩子->左孩子),隻需要單旋轉
	if (lchild->m_factor == 1)
	{
		tree->m_factor = 0;
		lchild->m_factor = 0;
		R_Rotate(tree);
	}
	// 如果不是偏向一邊(根->左孩子->右孩子),則需要雙選
	// 即先将【左子樹】左旋轉,再将樹右旋轉
	else if (lchild->m_factor == -1)
	{
		BBNode* lchild_rchild = lchild->m_rchild;
		// 修改【根結點】和【左孩子】的【平衡因子】
		switch (lchild_rchild->m_factor)
		{
		case -1:
			{
				tree->m_factor = 0;
				lchild->m_factor = 1;
			}break;
		case 0:
			{
				tree->m_factor = 0;
				lchild->m_factor = 0;
			}break;
		case 1:
			{
				tree->m_factor = -1;
				lchild->m_factor = 0;
			}break;
		}
		lchild_rchild->m_factor = 0;
		L_Rotate(lchild);
		R_Rotate(tree);
	}
}
           
/*
       a                   a                       c
	    \                   \                     / \
         b       =>          c          =>       a   b
	    /                     \
	   c                       b
*/
// 調整右子樹的,兩種情況
// 1:隻需要單旋轉-左旋轉
// 2: 雙旋轉-【右子樹】先右旋轉-再左旋轉
void RightBalance(BBSTree& tree)
{
	BBNode* rchild = tree->m_rchild;
	// 如果是偏向一邊的(根->右孩子->右孩子)
	if (rchild->m_factor == -1)
	{
		tree->m_factor = 0;
		rchild->m_factor = 0;
		L_Rotate(tree);
	}
	// 如果不是偏向一邊的(根->右孩子->左孩子)
	else if (rchild->m_factor == 1)
	{
		BBNode* rchild_lchild = rchild->m_lchild;
		switch (rchild_lchild->m_factor)
		{
		case -1:
			{
				tree->m_factor = 1;
				rchild->m_factor = 0;
			}break;
		case 0:
			{
				tree->m_factor = 0;
				rchild->m_factor = 0;
			}break;
		case 1:
			{
				tree->m_factor = 0;
				rchild->m_factor = -1;
			}break;
		}
		rchild_lchild->m_factor = 0;
		L_Rotate(rchild);
		R_Rotate(tree);
	}
}
           
// @tree:平衡二叉樹
// @e:要插入的元素
// @taller:是否增高了
int insertAVL(BBSTree& tree, int e, bool& taller)
{
	// 如果tree不存,則在此處插入結點
	if (!tree)
	{
		BBNode* node = new BBNode;
		node->m_data = e;
		node->m_factor = 0;	//新結點沒有左右孩子,factor自然為0
		node->m_lchild = 0;
		node->m_rchild = 0;
		tree = node;
		// 隻要是新結點,taller都标記為true,但是它實際上存在着下面三種情況的:
		// 1:如果父節點沒有左右孩子,則插入node,真的右增高
		// 2:如果父節點存在左孩子,插入node作為右孩子,則實際上并未增高
		// 3:如果父節點存在右孩子,插入node作為左孩子,則實際上并未增高
		// 但是這些都留待上一層的函數裡去處理
		taller = true;
	}
	else
	{
		// 要插入的元素小于目前結點
		if (e < tree->m_data)
		{
			// 将元素插入到目前結點的左子樹中
			if (!insertAVL(tree->m_lchild, e, taller))
			{
				// 如果插入失敗
				return 0;
			}
			// 子樹是不是增高了
			if (taller)
			{
				switch (tree->m_factor)
				{
				// 如果之前【右子樹】高于【左子樹】
				case -1:
					{
						// 因為插入的是【左子樹】,是以兩邊的深度相等了
						tree->m_factor = 0;
						// 當然,因為插入元素之後,高度不變
						taller = false;
					}break;
				// 如果之前【左子樹】高度等于【右子樹】
				case 0:
					{
						// 因為插入的是【左子樹】,是以目前【左子樹】高于【右子樹】
						// 但是還未超過【平衡二叉樹】【左右子樹】高度差不大于1的限制
						tree->m_factor = 1;
						taller = true;
					}
				// 如果之前【左子樹】高度大于【右子樹】
				case 1:
					{
						// 目前插入的是【左子樹】,是以目前【左子樹】高于了【右子樹】,
						// 并且超過了【平衡二叉樹】【左右子樹】高度差不大于1的限制
						// 是以要調整下
						LeftBalance(tree);
						taller = false;
					}
				}  // switch
			}  // if(taller)
		}
		else if (e == tree->m_data)
		{
			return 0;
		}
		else
		{
			if (!insertAVL(tree->m_rchild, e, taller))
			{
				return 0;
			}
			if (taller)
			{
				switch(tree->m_factor)
				{
				// 如果之前【左子樹】低于【右子樹】
				case -1:
					{
						// 因為目前插入的是【右子樹】,是以【右子樹】的高度
						// 超過了【平衡二次樹】的【左右子樹】深度差不超過1的限制
						// 是以要調整下
						RightBalance(tree);
						taller = false;
					}break;
				// 如果之前【左子樹】高度等于【右子樹】
				case 0:
					{
						// 因為目前插入的是【右子樹】,是以【右子樹】的高度增高了,
						// 但是不會超過【平衡二次樹】的【左右子樹】深度差不超過1的限制
						tree->m_factor = -1;
						taller = true;
					}break;
				// 如果之前【左子樹】高于【右子樹】
				case 1:
					{
						// 因為目前插入的是【右子樹】,是以兩邊的高度相等了
						tree->m_factor = 0;
						taller = false;
					}break;
				}
			}
		}  // else
	}  // else
	return 1;
}
 
           
// 中序周遊
void orderTraver(BBSTree tree)
{
	if (tree)
	{
		orderTraver(tree->m_lchild);
		cout<<tree->m_data<<" ";
		orderTraver(tree->m_rchild);
	}
}
           
int main()
{
	BBSTree tree = 0;
	bool taller;
	insertAVL(tree, 1, taller);
	insertAVL(tree, 2, taller);
	insertAVL(tree, 3, taller);
	insertAVL(tree, 4, taller);
	insertAVL(tree, 5, taller);
	insertAVL(tree, 6, taller);
	orderTraver(tree);
	return 0;
}
           

繼續閱讀