AVL树的增删查及其基本思想的理解

看了很多人写的,除了少部分写得较好以外,其他写的乱七八糟

决定还是得自己写一篇以方便日后复习(特别的有关红黑树的)

ps:关于旋转,这里不说明(附有源码,看下即可,应该比较好理解),大概说一下核心点就是

通过旋转可以使一边子树的高度+1,另一边-1

比如左旋就是时右边高度-1,左边高度+1

AVL树的定义:
左右子树高度差不超过1的树

因此我们只需要通过调整树的结构时其左右高度保存平衡即可

结构定义

template<class T>
class AVLTreeNode
{
public:
	AVLTreeNode<T>* _left;
	AVLTreeNode<T>* _right;
	AVLTreeNode<T>* _parent;

	T _data;
	int _bf;//平衡因子
public:
	AVLTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _bf(0)
	{}
};

我们通过平衡因子的方式去控制是否需要调整

左子树高度增加,则_bf--

右子树高度增加,则_bf++

所以在AVL树中,abs(_bf)<=1

//定义一个默认比较函数
template<class T>
class cmp
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x>y;
	}
};


//考虑到V可能是pair类型,我们需要用K来查询
template<class K, class V, class KeyofT = cmp<K>>
class AVLTree
{
	typedef AVLTreeNode<V> Node;
protected:
	//成员定义
	Node* _head;
	//定义一个转换成key的方法,这样写一次就可以了
	KeyofT  cmp;

public:
	//需要重写的默认生成函数
	AVLTree()
		:_head(nullptr)
	{}
	~AVLTree()
	{}
public:
//.....增删改的函数实现
};

基本结构框架

关于插入:

总的来说,我们只需关注这一件事(我这里在删除时的大思想也是类似的)

假设cur是新增节点,parent是它的父节点

那么parent在cur这一子树方向上是多了一个节点的

即我们需要改变parent的平衡因子

为了方便讨论,这里只说明cur是parent的左子树的情况(右子树完全类似)

情况1:如果parent的平衡因子原本为0,则插入后parent变-1

以parent为根的树整体高度增加,需要继续向上调整

情况2:如果parent的平衡因子原本为1,则插入后parent变为0

以parent为根的树左右两边已经平衡,由于总高度没有增加,所以不用继续向上调整

情况3:如果parent的平衡因子原本为-1,则插入后parent变为-2
此时该树的结构已经不平衡,需要进行旋转处理(这种情况是通过情况1演变过来的)

旋转分为4种情况

第一种:
parent->_bf==-2&&cur->_bf==-1

此时单纯左边高

我们只需进行右旋即可

第二种:
parent->_bf==2&&cur->_bf==1

此时单纯右边高

我们只需要进行左旋即可

第三种:

parent->_bf==-2&&cur->_bf==1

此时parent的左边高,cur是右边高

我们需要先将cur进行左旋,变成都是左边高

再整体右旋

第四种:
parent->_bf==2&&cur->_bf==-1

此时parent的右边高,cur是左高

我们需要先将cur进行右旋,变成都是右边高,

再整体左旋

第三种跟第四种,简单理解就是要变成第一种或第二种情况处理

 简单画个图方便理解

对于平衡因子的调整,建议画图自行分析,看一下是怎么旋转的就可知道_bf是怎么调整的了

插入函数

bool insert(const V& data)
	{
		if (_head == nullptr)
		{
			//头结点为空
			_head = new Node(data);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _head;
		while (cur)
		{
			if (cmp(cur->_data, data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cmp(data, cur->_data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				//相等,这里不考虑重复值,因此不进行插入
				return false;
			}
		}
		//创建节点
		cur = new Node(data);
		if (cmp(parent->_data, data))
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;
		//由于高度发生变化,需要继续向上调整
		//调整平衡
		while (parent)
		{
			if (parent->_left == cur)
			{
				//说明左子树多了个节点
				parent->_bf--;
			}
			else
			{
				//说明右子树多了个节点
				parent->_bf++;
			}

			if (parent->_bf == 0)
			{
				//说明之前左右存在高度查,现在左右平衡了,总体高度没有增加,可以break了

				break;
			}


			if (abs(parent->_bf) == 1)
			{
				//说明以parent为根节点的子树有一边高度增加了,需要继续向上调整
				cur = parent;
				parent = cur->_parent;
				continue;
			}
			if (abs(parent->_bf) == 2)
			{
				//说明此时已经不平衡,需要进行旋转调整处理
				if (parent->_bf == 2 && cur->_bf == 1)
				{
					//parent,cur均是右边高,进行左单旋即可
					RotateL(parent);
					//旋转后修正平衡因子
					parent->_bf = cur->_bf = 0;
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值