平衡树(AVL树)

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可以降低树的高度,从而减少平均搜索长度。

AVL树

一颗AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  • 它的左右子树都是AVL树
  • 左右子树高度差(简称平衡因子)的绝对值不超过1(1、-1、0)

在这里插入图片描述
如果一颗二叉搜索树是高度平衡的,它就是AVL树。如果他有n个结点,其高度可保持在O(lgn),平均搜索的时间复杂度是O(lgn)

如果一棵树原来是平衡的二叉搜索树,现在向里面插入一个节点,造成了不平衡,这时候我们就要调整这棵树的结构,使之重新平衡。

规则:

  • 如果插入一个结点,树的高度不变,则它就是平衡的
  • 旋转之后树的高度不变,这里的不变是指与插入之前的高度相同
  • 插入之后只会影响从插入点到根节点路径上的结点的平衡
  • 平衡因子的取值只可能是-2,-1,0,1,2,取-2或2时就出现不平衡了,这时就需要调整,调整之后树就平衡
  • 所有的插入都是建立在平衡二叉树的基础之上的

AVL树有三个值,一个_key(需要插入节点的值)、一个_value(某插入节点的下标)、一个_bf(平衡因子)。三个指针,左孩子指针,右孩子指针,父亲指针。

平衡二叉树结点的定义:

template<typename K, typename V>
struct AVLTreeNode
{
	K _key;
	V _value;

	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	int _bf;

	AVLTreeNode(const K& key, const V& value)
		:_key(key)
		, _value(value)
		, _bf(0)
		, left(NULL)
		, _right(NULL)
		, _parent(NULL)
	{}
};

AVL树平衡调整

  • 找平衡因子等于2 / -2
  • 找插入新结点后失去平衡的最小子树
    1) 距离插入结点最近
    2) 平衡因子绝对值大于1的结点作为根
  • 平衡调整

(1)左单旋【RR形 中为支,高左转】

当在子树c上插入一个新节点.此时子树c的高度变化为h+1,子树b的高度为h.结点5的平衡因子变为1,结点3的平衡因子变为2.此树不再平衡

左单旋:整体向左方向旋转。【RR形 中为支,高左转】

  • 结点5的左子树b变为结点3的右子树
  • 结点3以及其子树变为结点5的左子树
    在这里插入图片描述
//左单旋 RR形
void RotateL(Node* parent)
{
	Node* subR = parent->_right; //subR为parent结点的左孩子
	Node* subRL = subR->_left;  //subRL为subR的左孩子

	parent->_right = subRL;   //将subRL变为parent的右子树

	//如果subRL非空 则设置它的父亲指针 即祖先结点
	if (subRL)
	{
		subRL->_parent = parent;
	}

	Node* ppNode = parent->_parent; //记录下parent的父亲结点 即祖先结点

	//将parent变为subR的左子树 并设置父亲指针
	subR->_left = parent;
	parent->_parent = subR;

	//设置ppNode 若不为空 则将其指向subR
	if (ppNode)
	{
		//判断parent是ppNode的左孩子还是右孩子 以方便设置subR的父亲结点
		if (ppNode->_left == parent)
			ppNode->_left = subR;
		else
			ppNode->_right = subR;
		subR->_parent = ppNode;
	}
	else  //若为空 则代表parent原来为根结点 则旋转后subR变为根节点
	{
		subR->_parent = NULL;
		_root = subR;
	}
	//设置平衡因子 置零
	subR->_bf = 0;
	parent->_bf = 0;
}

(2)右单旋【LL形 中为支,高右转】
当在子树a的下面插入一个结点时,结点3的左子树高度变为h+1,右子树高度为h,平衡因子变为-1.结点5的左子树高度变为h+2,右子树高度变为h,平衡因子变为-2.此时不再平衡。

右单旋:整体向右方向旋转。【LL形 中为支,高右转】

  • 结点5变为结点3的右子树
  • 结点3以及其子树变为结点5的左子树
  • parent指向结点3
    在这里插入图片描述
void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	parent->_left = subLR;

	if (subLR)
	{
		subLR->_parent = parent;
	}

	Node* ppNode = parent->_parent;

	subL->_right = parent;
	parent->_parent = subL;

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

(3)左右双旋【下二整体先左转 后右单旋】

新结点插入到子树b和c会有不同的情况,首先以在子树b下插入一个结点为例,此时结点4的平衡因子变为-1,结点3的平衡因子变为1,结点5的平衡因子变为-2. AVL树不再平衡。

左右双旋:先左单旋,再右单旋。不过两次的对象不同。先对结点3和结点4进行左单旋,然后进行右单旋。

在这里插入图片描述

新结点插入到子树b和c会有不同的情况:
(1)插入到b树下:

也就是我刚刚举例子的图:

在这里插入图片描述

(2)插入到c树下:
在这里插入图片描述

(3)a、b、c、d子树都为NULL,插入的结点本就是subLR

在这里插入图片描述

左右双旋结点插入小结:

  • 结点subLR的左子树b都变成了结点subL的右子树
  • 结点subRL的右子树c都变成了结点parent的左子树
  • 平衡因子的计算公式为,_bf = 右子树高度-左子树高度

平衡因子的变化情况:

在这里插入图片描述

void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = parent->_right;

	int bf = subLR->_bf;  //记录下旋转前subLR的平衡因子

	//左右双旋
	RotateL(parent->_left);
	RotateR(parent);

	3种情况
	if (bf == 0) //情况三 a b c d都为NULL 且插入的结点就是subLR
	{
		parent->_bf = 0;
		subL->_bf = 0;
		subLR->bf = 0;
	}
	else if (bf == -1) //情况一 b子树下插入新结点
	{
		parent->_bf = 1;
		subL->_bf = 0;
		subLR->_bf = 0;
	}
	else  //情况二 c子树下插入新结点
	{
		parent->_bf = 0;
		subL->_bf = -1;
		subLR->_bf = 0;
	}
}

(4)右左双旋【下二整体先右转 后左单旋】

同样也是有三种情况,这里我以新节点插入到c子树为例分析:

在这里插入图片描述
调整过程:
在这里插入图片描述
这里我直接给出三种情况平衡因子的总结:

在这里插入图片描述

void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf; //记录旋转前subRL的平衡因子 

		RotateR(subR);
		RotateL(parent);
		if (bf == 0) //情况三
		{
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else if(bf == 1) //情况一
		{
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else
		{
			subR->_bf = 1;
			parent->_bf = 0;
		}
		subRL->_bf = 0;
	}

完整的平衡二叉树类


template<typename K, typename V>
struct AVLTreeNode
{
	K _key;
	V _value;

	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	int _bf;

	AVLTreeNode(const K& key, const V& value)
		:_key(key)
		, _value(value)
		, _bf(0)
		, _left(NULL)
		, _right(NULL)
		, _parent(NULL)
	{}
};

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
	//typedef AVLTreeNode<K, V>* PNode;

public:
	AVLTree()
		:_root(NULL)
	{}
	AVLTree(const AVLTree<K, V>& tree)
		:_root(NULL)
	{
		_Copy(tree._root, _root);
	}
	AVLTree<K, V>& operator=(const AVLTree<K, V>& avl)
	{
		if (this != &avl)
		{
			AVLTree<K, V> tmp(avl);
			swap(_root, tmp._root);
		}
		return *this;
	}
	~AVLTree()
	{
		_Destory(_root);
	}
	bool Insert(const K& key, const V& value)
	{
		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;             //如果已经存在就不插入
		}
		cur = new Node(key, value);
		if (parent != NULL)                 //树不是空树
		{
			if (parent->_key < key)
				parent->_right = cur;
			else
				parent->_left = cur;
			cur->_parent = parent;
		}
		else                             //树是空树
		{
			_root = cur;
			return true;
		}
		if (cur == parent->_left)        //如果插入的位置是父节点的左孩子
			parent->_bf--;
		else
			parent->_bf++;               //如果插入的位置是父节点的右孩子
		while (parent != NULL)             //沿着插入位置到根节点回溯,查找平衡因子不满足的结点
		{
			if (parent->_bf == 0)          //表明插入之后parent这棵树的高度没变,因此这时就是AVL树
			{
				return true;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)  //parent这棵树满足AVL,因为parent的高度发生变化,还要向上查找祖先结点
			{
				Node* ppNode = parent->_parent;      //保存parent的父节点
				if (ppNode != NULL)
				{
					if (ppNode->_left == parent)      //ppNode的左子树高度发生变化
						ppNode->_bf--;
					else                              //ppNode的右子树高度发生变化
						ppNode->_bf++;
				}
				parent = parent->_parent;      //当前树满足AVL树,所以继续向上判断
			}
			else          //如果当前树不满足AVL树,则就进行旋转恢复平衡
			{
				if (parent->_bf == 2)          //如果parent的右子树高
				{
					if (parent->_right->_bf == 1)      //parent一定不是叶子节点,所以他的孩子不为空
						RotateL(parent);             //因为子树与parent一样都是右子树高,进行单左旋
					else                       //parent的右子树高,它的孩子左子树高,右左双旋
						RotateRL(parent);
				}
				else if (parent->_bf == -2)         //parent的左子树高度高
				{
					if (parent->_left->_bf == -1)  //parent孩子的左子树高
						RotateR(parent);          //右单旋
					else                   //parent的左子树高,它的孩子右子树高,左右双旋
						RotateLR(parent);
				}
				break;                      //旋转之后这棵树的高度恢复成原来的高度
			}
		}
		return true;
	}
	bool Remove(const K& key)
	{
		Node* parent = NULL;
		Node* cur = _root;
		Node* del = NULL;        //用来指向要删除的位置
		while (cur)                  //寻找要删除的位置
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
				break;
		}
		if (cur == NULL)                 //删除失败
			return false;
		del = cur;
		//如果要删除的结点有两个孩子,则寻找右子树最左结点,将问题转换成只有一个孩子或没有孩子的形式
		if (cur->_left != NULL && cur->_right != NULL)
		{
			cur = cur->_right;          //cur的右孩子一定不为空
			while (cur->_left)
			{
				cur = cur->_left;
			}
			del->_key = cur->_key;
			del->_value = cur->_value;
			del = cur;           //交换之后让del指向这个要删除的结点
		}
		parent = cur->_parent;        //让parent指向要删除的结点的父亲
		if (cur->_left == NULL)       //要删除的结点的左孩子为空,或者都为空
		{
			if (parent == NULL)
			{
				_root = cur->_right;
				if (cur->_right)           //如果cur有右孩子,则右孩子直接做根
					cur->_right->_parent = NULL;
			}
			else
			{
				if (parent->_left == cur)
					parent->_left = cur->_right;
				else
					parent->_right = cur->_right;
				if (cur->_right)                    //cur的右子树不为空
					cur->_right->_parent = parent;
			}
			cur = del->_right;           //cur更新到要删除结点的右子树
		}
		else                        //要删除的结点的右孩子为空,左孩子不为空
		{
			if (parent == NULL)               //如果要删除的结点是头结点
				_root = cur->_left;
			else                                    //删除的结点不是根节点,则进行链接
			{
				if (parent->_left == cur)
					parent->_left = cur->_left;
				else
					parent->_right = cur->_left;
				cur->_left->_parent = parent;
			}
			cur = del->_left;            //cur更新到要删除结点的左子树
		}
		//因为要删除的结点之后的子树的高度不变,不需要修改,需要从parent向上判断是否平衡
		while (parent)           //只要parent不为空,就有可能不平衡
		{
			//调整parent的平衡因子
			if (parent->_left == cur)   //删除的是parent的左子树
				parent->_bf++;
			else
				parent->_bf--;
			if (parent->_bf == 1 || parent->_bf == -1) //原来平衡,删除一个后树高度不变,整棵树已经平衡
			{
				break;
			}
			//平衡因子原来不为0,删除一个高的子树之后变为0,需要继续向上寻找
			if (parent->_bf != 0)    //平衡因子原来不为0,且比较矮的子树被删除
			{
				if (cur == NULL)
				{
					if (parent->_left == NULL)
						cur = parent->_right;
					else
						cur = parent->_left;
				}
				else
				{
					if (parent->_left == cur)   //原来parent的比较矮的左子树被删除,让cur指向较高的子树
						cur = parent->_right;
					else
						cur = parent->_left;
				}
				if (cur->_bf == 0)       //cur的平衡因子为0,单旋转就可以平衡,而且parent这棵树的高度不变
				{
					if (parent->_bf < 0)  //parent的左子树高,进行右旋转
					{
						RotateR(parent);        //parent旋转后指向这棵旋转子树的新根
						parent->_bf = 1;
						parent->_right->_bf = -1;
					}
					else          //进行左旋转
					{
						RotateL(parent);
						parent->_bf = -1;
						parent->_left->_bf = 1;
					}
					break;
				}
				//如果parent与较高的子树同号,则进行单旋转,旋转自后树的高度没有恢复成删除之前的,所以继续向上找
				int d = parent->_bf - cur->_bf;   //用d来判断是否同号,同号的话为1或-1,因为parent->_bf为2或-2,cur->_bf为1或-1
				if (d == 1 || d == -1)
				{
					if (d == 1)       //右子树高,要进行左旋
						RotateL(parent);
					else
						RotateR(parent);      //左子树高,进行右旋
				}
				else              //要双旋处理,因为异号,所以d为3或-3
				{
					if (d == 3)       //parent->_bf为2,cur->_bf为-1,右左双旋
						RotateRL(parent);
					else
						RotateLR(parent);
				}
			}
			cur = parent;
			parent = parent->_parent;
		}
		delete del;
		return true;
	}
	bool Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
				cur = cur->_right;
			else if (cur->_key > key)
				cur = cur->_left;
			else
				return true;
		}
		return false;
	}
	bool IsAVLTree()
	{
		int Height = 0;
		return _IsAVLTree(_root, Height);
	}

	void InOrder()
	{
		_InOrder(_root);
	}
protected:
	void _Copy(Node* root, Node* &newroot)
	{
		if (root == NULL)
		{
			return;
		}
		Node* node = new Node(root->_key, root->_value);
		node->_bf = root->_bf;
		newroot = node;
		node->_parent = newroot;
		_Copy(root->_left, newroot->_left);
		_Copy(root->_right, newroot->_right);
	}
	void _Destory(Node* root)
	{
		if (root == NULL)
		{
			return;
		}
		_Destory(root->_left);
		_Destory(root->_right);
		delete root;
	}
	//左单旋 RR形
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right; //subR为parent结点的左孩子
		Node* subRL = subR->_left;  //subRL为subR的左孩子

		parent->_right = subRL;   //将subRL变为parent的右子树

		//如果subRL非空 则设置它的父亲指针 即祖先结点
		if (subRL)
		{
			subRL->_parent = parent;
		}

		Node* ppNode = parent->_parent; //记录下parent的父亲结点 即祖先结点

		//将parent变为subR的左子树 并设置父亲指针
		subR->_left = parent;
		parent->_parent = subR;

		//设置ppNode 若不为空 则将其指向subR
		if (ppNode)
		{
			//判断parent是ppNode的左孩子还是右孩子 以方便设置subR的父亲结点
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else
				ppNode->_right = subR;
			subR->_parent = ppNode;
		}
		else  //若为空 则代表parent原来为根结点 则旋转后subR变为根节点
		{
			subR->_parent = NULL;
			_root = subR;
		}
		//设置平衡因子 置零
		subR->_bf = 0;
		parent->_bf = 0;
	}
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;

		if (subLR)
		{
			subLR->_parent = parent;
		}

		Node* ppNode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (ppNode)
		{
			if (ppNode->_left == parent)
				ppNode->_left = subL;
			else
				ppNode->_right = subL;
			subL->_parent = ppNode;
		}
		else
		{
			subL->_parent = NULL;
			_root = subL;
		}
		subL->_bf = 0;
		parent->_bf = 0;
	}
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = parent->_right;
		int bf = 0;
		if (subLR == NULL)
			bf = 0;
		else
			bf = subLR->_bf;  //记录下旋转前subLR的平衡因子

		//左右双旋
		RotateL(parent->_left);
		RotateR(parent);

		//3种情况
		if (bf == 0) //情况三 a b c d都为NULL 且插入的结点就是subLR
		{
			parent->_bf = 0;
			subL->_bf = 0;
		}
		else if (bf == -1) //情况一 b子树下插入新结点
		{
			parent->_bf = 1;
			subL->_bf = 0;
		}
		else  //情况二 c子树下插入新结点
		{
			parent->_bf = 0;
			subL->_bf = -1;
		}
		//subLR->_bf = 0;
	}
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf; //记录旋转前subRL的平衡因子 

		RotateR(subR);
		RotateL(parent);
		if (bf == 0) //情况三
		{
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else if(bf == 1) //情况一
		{
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else
		{
			subR->_bf = 1;
			parent->_bf = 0;
		}
		subRL->_bf = 0;
	}
	void _InOrder(Node* root)
	{
		if (root == NULL)
			return;
		_InOrder(root->_left);
		cout << root->_key << " " << root->_bf << endl;
		_InOrder(root->_right);
	}
	int _Hight(Node* root)
	{
		if (root == NULL)
		{
			return 0;
		}
		int left = _Hight(root->left) + 1;
		int right = _Hight(root->right) + 1;

		return left > right ? left : right;
	}
	bool _IsAVLTree(Node *root, int &Height)
	{
		if (root == NULL)
		{
			Height = 0;
			return true;
		}

		int leftHeight = 0;
		if (_IsAVLTree(root->_left, leftHeight) == false)
		{
			return false;
		}
		int rightHeight = 0;
		if (_IsAVLTree(root->_right, rightHeight) == false)
		{
			return false;
		}

		Height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;

		return abs(leftHeight - rightHeight) < 2;
	}
private:
	Node* _root;
};
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值