浅析二叉平衡树之AVL树

在前面的文章中介绍了二叉搜索树,尽管它可以缩短查找的速率,但是在数据有序或者接近有序的情况下,二叉搜索树就会退化成一棵单支树,查找的效率就会变得低下。因此,就有两位伟大的数学家发明了一种新的数据结构——AVL树,解决了二叉搜索树的一些不足。

什么是AVL树?(概念)

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
1、它的左右子树都是AVL树
2、左子树和右子树高度之差(简称平衡因子)的绝对值不超过1(-1、0、1)

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(lgn),平均搜索时间复杂度O(lg(n))。

如图:


AVL树的插入:

①若是空树,则直接插入;

②若不是空树,寻找插入位置,若树中有相同的数据则不进行插入,直接返回;

③插入成功后要检查平衡因子,必须要满足平衡因子的绝对值小于2;

④更新平衡因子,对树进行调整;

在根据平衡因子调整树的情况分为一下几种:

假设新插入的节点为p(新插入的节点的平衡因子为0),其双亲节点为pr

情况一:结点Pr的平衡因子为0
在Pr的较矮的子树上插入新节点,结点Pr平衡,其高度没有增加,此时从Pr到根路径上各结点为根的子树的高度不变,即各结点的平衡因子不变,结束平衡化处理。

情况二:结点Pr的平衡因子的绝对值为1;
插入前Pr的平衡因子为0,插入后以Pr为根的子树没有失去平衡,但该子树的高度增加,需从该结点Pr向根节点方向回溯,继续查看Pr的双亲结点的平衡性。

情况三:结点Pr的平衡因子的绝对值为2(说明经过了情况二的回溯)新节点在较高的子树插入,需要做平衡化处理:
若Pr = 2,说明右子树高,设Pr的右子树为q;
当q的平衡因子为1,执行左单旋转;
当q的平衡因子为-1,执行先右后左双旋转;
若Pr = -2,说明左子树高,设Pr的左子树为q;
当q的平衡因子为-1,执行右单旋转;
当q的平衡因子为1,执行先左后右双旋转;
旋转后Pr为根的子树高度降低,无需继续向上层回溯;


接下来我们主要看这几种旋转的情况:

左单旋:


        //左单旋
	void _RotatelL(Node* parent)
	{
		assert(parent);
		Node* SubR = parent->_right;
		Node* SubRL = SubR->_left;
		Node* pparent = parent->_parent;

		parent->_right = SubRL;
		if (SubRL)//判断SubRL是否为空
			SubRL->_parent = parent;

		SubR->_left = parent;
		parent->_parent = SubR;
		//判断parent是否为根节点
		if (pparent == NULL)//parent是根节点
		{
			_root = SubR;
			_root->_parent = NULL;
		}
		else//parent不是根节点
		{
			if (parent == pparent->_left)
			{
				pparent->_left = SubR;
			}
			else
			{
				pparent->_right = SubR;
			}
			SubR->_parent = pparent;
		}
		parent->bf = SubR->bf = 0;
	}

右单旋:


//右单旋
	void _RotatelR(Node* parent)
	{
		assert(parent);
		Node* SubL = parent->_left;
		Node* SubLR = SubL->_right;
		Node* pparent = parent->_parent;

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

		SubL->_right = parent;
		parent->_parent = SubL;
		if (pparent == NULL)//如果parent是根节点
		{
			_root = SubL;
			_root->_parent = NULL;
		}
		else//不是根节点
		{
			if (parent == pparent->_left)
				pparent->_left = SubL;
			else
				pparent->_right = SubL;

			SubL->_parent = pparent;
		}
		parent->bf = SubL->bf = 0;
	}

左右双旋


左右双旋后,平衡因子有以下几种情况;

情况一:SubLR即为插入节点


情况二:插入的节点为SubLR的右节点


情况三:插入的节点为SubLR的左节点


//左右双旋
	void _RotatelLR(Node* parent)
	{
		assert(parent);
		Node* SubL = parent->_left;
		Node* SubLR = SubL->_right;
		int bf = SubLR->bf;
		_RotatelL(SubL);//先左旋
		_RotatelR(parent);//再右旋

		if (bf == 0)//说明SubLR为新插入的节点
			parent->bf = SubL->bf = 0;
		else if (bf == 1)//说明插入的节点在SubLR的右边
		{
			SubL->bf = -1;
			parent->bf = 0;
		}
		else//说明插入的节点在SubLR的左边
		{
			SubL->bf = 0;
			parent->bf = 1;
		}

		SubLR->bf = 0;//最后,SubLR的孩子节点即左孩子或右孩子都分走了,因此要将SubLR的bf置0
	}

左右双旋和单旋不同的是,平衡因子变化会与插入的节点位置有关即与SubLR的平衡因子有关,分为三种情况:①SubLR为插入点;②在SubLR的右边插入;③在SubLR的左边插入;这三种情况对应的SubLR的平衡因子有三种0、1和-1;因此对应parent和SubL的平衡因子就有三种情况:


右左双旋和左右双旋类似,这里就不多阐述。

以下是全部的源码:

#include<iostream>
#include<cassert>
using namespace std;


template<class T, class V>
struct AVLTreeNode
{
	AVLTreeNode<T, V>* _left;
	AVLTreeNode<T, V>* _right;
	AVLTreeNode<T, V>* _parent;
	T _key;
	V _value;
	int bf;//平衡因子

	AVLTreeNode(const T& key, const V& value)
		:_left(NULL)
		, _right(NULL)
		, _parent(NULL)
		, _key(key)
		, _value(value)
		, bf(0)
	{}

};

template<class T, class V>
class AVLTree
{
	typedef AVLTreeNode<T, V> Node;
public:
	AVLTree()
		:_root(NULL)
	{}

	~AVLTree()
	{
		Destory(_root);
	}

	//插入
	bool Insert(const T& key, const V& value)
	{
		//如果根节点,直接插入
		if (_root == NULL)
		{
			_root = new Node(key,value);
			return true;
		}
		//不是根节点
		Node* cur = _root;
		Node* parent = NULL;
		//寻找插入位置
		while (cur)
		{
			if (key < cur->_key)//向左
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (key > cur->_key)//向右
			{
				parent = cur;
				cur = cur->_right;
			}
			else//树里已经有key,直接返回
			{
				return false;
			}
		}
		//找到了要插入的位置
		cur = new Node(key,value);
		//判断要插入的位置在双亲节点的左/右
		if (key < parent->_key)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		//插入后需要更新平衡因子
		while (parent)
		{
			//先更新双亲节点的平衡因子
			if (parent->_left == cur)
				parent->bf--;
			else
				parent->bf++;

			//更新完后检查双亲的平衡因子是否平衡
			if (parent->bf == 0)//平衡因子为0则平衡
				return true;
			else if (parent->bf == 1 || parent->bf == -1)//平衡因子绝对值为1,											
			{                                            //则要判断parent的双亲节点的平衡因子,进行向上回溯
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->bf == 2 || parent->bf == -2)//不平衡则进行调整
			{
				//分两种情况
				//第一种
				if (parent->bf == 2)
				{
					if (cur->bf == 1)//判断是否同号
					{
						_RotatelL(parent);//左旋
						//return true;
					}
					else//cur->bf == -1 异号
					{
						_RotatelRL(parent);
						//return true;
					}
				}
				//第二种
				else if (parent->bf == -2) 
				{
					if (cur->bf == -1)//同号
					{
						_RotatelR(parent);
						//return true;
					}
					else // 异号 cur->bf == 1
					{
						_RotatelLR(parent);
						//return true;
					}
				}
				break;
			}
		}
		return true;
	}

	bool Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key < cur->_key)
				cur = cur->_left;
			else if (key > cur->_key)
				cur = cur->_right;
			else
				return true;
		}
		return false;
	}

	Node* Destory(Node* root)
	{
		if (root == NULL)
			return NULL;
		else
		{
			Destory(root->_left);
			Destory(root->_right);
			delete root;
			root = NULL;
		}
		return root;
	}

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

	//判断平衡
	bool Isbalance()
	{
		int hight = 0;
		return _Isbalance(_root, hight);
	}

private:
	//左单旋
	void _RotatelL(Node* parent)
	{
		assert(parent);
		Node* SubR = parent->_right;
		Node* SubRL = SubR->_left;
		Node* pparent = parent->_parent;

		parent->_right = SubRL;
		if (SubRL)//判断SubRL是否为空
			SubRL->_parent = parent;

		SubR->_left = parent;
		parent->_parent = SubR;
		//判断parent是否为根节点
		if (pparent == NULL)//parent是根节点
		{
			_root = SubR;
			_root->_parent = NULL;
		}
		else//parent不是根节点
		{
			if (parent == pparent->_left)
			{
				pparent->_left = SubR;
			}
			else
			{
				pparent->_right = SubR;
			}
			SubR->_parent = pparent;
		}
		parent->bf = SubR->bf = 0;
	}
	//右单旋
	void _RotatelR(Node* parent)
	{
		assert(parent);
		Node* SubL = parent->_left;
		Node* SubLR = SubL->_right;
		Node* pparent = parent->_parent;

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

		SubL->_right = parent;
		parent->_parent = SubL;
		if (pparent == NULL)//如果parent是根节点
		{
			_root = SubL;
			_root->_parent = NULL;
		}
		else//不是根节点
		{
			if (parent == pparent->_left)
				pparent->_left = SubL;
			else
				pparent->_right = SubL;

			SubL->_parent = pparent;
		}
		parent->bf = SubL->bf = 0;
	}
	//左右双旋
	void _RotatelLR(Node* parent)
	{
		assert(parent);
		Node* SubL = parent->_left;
		Node* SubLR = SubL->_right;
		int bf = SubLR->bf;
		_RotatelL(SubL);//先左旋
		_RotatelR(parent);//再右旋

		if (bf == 0)//说明SubLR为新插入的节点
			parent->bf = SubL->bf = 0;
		else if (bf == 1)//说明插入的节点在SubLR的右边
		{
			SubL->bf = -1;
			parent->bf = 0;
		}
		else//说明插入的节点在SubLR的左边
		{
			SubL->bf = 0;
			parent->bf = 1;
		}

		SubLR->bf = 0;//最后,SubLR的孩子节点即左孩子或右孩子都分走了,因此要将SubLR的bf置0
	}
	//右左双旋
	void _RotatelRL(Node* parent)
	{
		assert(parent);
		Node* SubR = parent->_right;
		Node* SubRL = SubR->_left;
		int bf = SubRL->bf;

		_RotatelR(SubR);
		_RotatelL(parent);

		if (bf == 0)//说明插入的节点是SubRL
			parent->bf = SubR->bf = 0;
		else if (bf == 1)//说明插入的节点在SubR的右边
		{
			SubR->bf = 0;
			parent->bf = -1;
		}
		else//说明插入的节点在SubR的左边
		{
			SubR->bf = 1;
			parent->bf = 0;
		}
		SubRL->bf = 0; //最后,SubRL的孩子节点即左孩子或右孩子都分走了,因此要将SubRL的bf置0
	}

	//中序遍历
	void _InOrder(Node* root)
	{
		if (root)
		{
			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}
	}

	//求树的高度
	size_t _height(Node* root)
	{
		if (root == NULL)
			return 0;

		size_t hightL = _height(root->_left)+1;
		size_t hightR = _height(root->_right) + 1;

		return hightL > hightR ? hightL : hightR;
	}

	//判断平衡
	bool _Isbalance(Node* root, int& hight)
	{
		if (root == NULL)
		{
			hight = 0;
			return true;
		}
			
		int hightL = 0;
		int hightR = 0;

		if (_Isbalance(root->_left, hightL) == false)
			return false;
		if (_Isbalance(root->_right, hightR) == false)
			return false;

		if (hightR - hightL != root->bf)
		{
			cout << "平衡因子异常" <<root->_key<< endl;
		}

		hight = hightL > hightR ? hightL+1 : hightR+1;
		return abs(hightL - hightR) < 2;
	}

	bool _Isbalance2(Node* root)
	{
		if (root == NULL)
			return true;

		int hightL = _Isbalance2(root->_left);
		int hightR = _Isbalance2(root->_right);

		return abs(hightL - hightR) < 2
			&& _Isbalance2(root->_left)
			&& _Isbalance2(root->_right);
	}

private:
	Node* _root;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值