数据结构——AVL树

作者:小 琛
欢迎转载,请标明出处

AVL树的概念与定义

二叉搜索树在很大程度上优化了查找的效率,但如果插入的数据非常有序(例如下图),则二叉搜索树就会退化成单一的链式结构,这种情况下的查找等操作就非常消耗时间。平衡二叉树就因此引入。
在这里插入图片描述

定义

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

对于每个节点的平衡因子值_bf,_bf=右子树高度-左子树高度。因此在一棵树的左边插入,平衡因子减一;右边插入平衡因子加一。

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

  • 它的左右子树都是AVL树
  • 左右子树的高度差(通常称之为平衡因子)绝对值<=1.
    在这里插入图片描述

AVL树的插入

步骤:

  • 按照二叉搜索树的规则进行插入
  • 调整平衡因子。
  • 检查平衡因子是否合理,如果不合理进行调整

二叉搜索树的插入之前博客有讲,这里不进行赘述,直接从插入后的情况进行分析。

插入后的三种情况

  • 情况1:
    在这里插入图片描述
  • 情况2:
    在这里插入图片描述
  • 情况3:
    在这里插入图片描述
    对于上面的三种情况,前两种满足AVL树的准则(高度差不大于1),而第三种,即不符合,这时候就需要进行"旋转调整"

四种不同情况下的旋转调整

  • 右单旋
    新节点插入较高左子树的左侧
    在这里插入图片描述
    旋转后结果:
    在这里插入图片描述
void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

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

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

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

			subL->_parent = ppNode;
		}

		parent->_bf = subL->_bf = 0;
	}

  • 左单旋
    新节点插入较高右子树的右侧
    在这里插入图片描述
    调整后结果:
    在这里插入图片描述
void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;
		Node* ppNode = parent->_parent;
		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else {
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else
				ppNode->_right = subR;

			subR->_parent = ppNode;
		}

		subR->_bf = parent->_bf = 0;
	}
  • 先左旋再又旋
    新节点插入较高左子树的右侧
    在这里插入图片描述
	void RotateLR(Node* parent)
	{
		RotateL(parent->_left);
		RotateR(parent);
	}
  • 先右旋再左旋
    新节点插入较高右子树的左侧
    在这里插入图片描述
void RotateRL(Node* parent)
	{
		RotateR(parent->_right);
		RotateL(parent);
	}

AVL树的评价

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保查询时高效的时间复杂度。

但是如果要对AVL树做一些结构修改的操作,性能非常低下。
比如:
插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但如果一个结构经常修改,就不太适合。

实现AVL树的总代码

#pragma once;

#include <iostream>


template <class K,class V>
struct AVLTreeNode
{
	std::pair<K, V> _kv;
	int _bf;// balance factor
	AVLTreeNode<K, V>* _parent;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode(const std::pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
		, _kv(kv)
	{}
};

template <class K,class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(std::pair<K, V>& kv)
	{
		//依照二叉搜索树的规则插入

		//空树的插入
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}
		//非空树插入
		Node* cur = _root;
		Node* parent = nullptr;//记录父节点
		//寻找插入的位置
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first>kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
				return false;
		}
		cur = new Node(kv);
		if (parent->_kv.first > kv.first)
		{
			cur->_parent = parent;
			parent->_left = cur;
		}
		else
		{
			cur->_parent = parent;
			parent->_right = cur;
		}

		//更新平衡因子
		while (parent)
		{
			if (parent->_left == cur)
				parent->_bf--;
			else if (parent->_right == cur)
				parent->_bf++;

			if (parent->_bf == 0)
				break;
			else if (std::abs(parent->_bf) == 1)
			{
				cur = parent;
				parent->_parent;
			}
			else if (std::abs(parent->_bf) == 2)
			{
				// 说明parent子树已经不平衡,需要旋转处理,让他平衡
				if (parent->_bf == 2)
				{
					if (cur->_bf == 1)
					{
						RotateL(parent);
					}
					else if (cur->_bf == -1)
					{
						RotateRL(parent);
					}
				}
				else if (parent->_bf == -2)
				{
					if (cur->_bf == -1)
					{
						RotateR(parent);
					}
					else if (cur->_bf == 1)
					{
						RotateLR(parent);
					}
				}

				break;
			}
			else
			{
				return false;
			}
		}

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

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;
		Node* ppNode = parent->_parent;
		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else {
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else
				ppNode->_right = subR;

			subR->_parent = ppNode;
		}

		subR->_bf = parent->_bf = 0;
	}

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

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

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

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

			subL->_parent = ppNode;
		}

		parent->_bf = subL->_bf = 0;
	}
	void RotateLR(Node* parent)
	{
		RotateL(parent->_left);
		RotateR(parent);
	}

	void RotateRL(Node* parent)
	{
		RotateR(parent->_right);
		RotateL(parent);
	}
private:
	Node* _root = nullptr;
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值