二叉搜索树——AVL树

前面对map和set做了简单的介绍,这几个的个共同特点就是其底层都是用二叉搜索树来写的,但是二叉搜索树有自身的缺陷,如果树中插入的元素有序或接近有序,二叉搜索树就会退化成单支树,时间复杂度变成O(N),所以map和set等关联式容器的底层结构是对二叉树做了平衡处理,采用AVL树实现

AVL树的概念

当向二叉搜索树插入新的节点后,如果能保证每个节点的左右子树高度差的绝对值不超过一,即是AVL树,如果超过一,就要进行调整,使其变成AVL树

AVL树节点的定义

template<calss T>
struct AVLTreeNode
{
AVLTreeNode(const T& data)
     : _Left(nullptr)
     , _Right(nullptr)
     , _Parent(nullptr)
     , _data(data)
     , _bf(0)
 {}
 AVLTreeNode<T>* _Left;   // 该节点的左孩子
 AVLTreeNode<T>* _Right;  // 该节点的右孩子
 AVLTreeNode<T>* _Parent; // 该节点的双亲
 T _data;
 int _bf;                  // 该节点的平衡因子
}

AVL树的插入

AVL树引入了平衡因子的概念,就是右子树高度-左子树高度

插入:

1.按照搜索树规则插入

2.更新祖先节点的平衡因子

a.插入父亲节点的右边,平衡因子++

b.插入父亲节点的左边,平衡因子--

c.父亲平衡因子==0,父亲所在子树高度不变,停止更新,插入结束

d.父亲平衡因子==1or-1,父亲所在子树高度变了,继续往上更新

e.父亲平衡因子==2or-2,父亲所在子树不平衡了,需要旋转处理

AVL树的旋转

当AVL树不平衡时,我们会进行旋转,一共有四种旋转方式:右单旋,左单旋,左右旋,右左旋

右单旋

右单旋对应的parent的平衡因子是-2,cur对应的平衡因子是-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;
		_root->_parent = nullptr;
	}
	else
	{
		if (ppNode->_left == parent)
		{
			ppNode->_left = subL;
		}
		else
		{
			ppNode->_right = subL;
		}
		subL->_parent = ppNode;
	}
	parent->_bf = subL->_bf = 0;
}

左单旋

左单旋对应的parent的平衡因子是2,cur对应的平衡因子是1

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;
		_root->_parent = nullptr;
	}
	else
	{
		if (ppNode->_left == parent)
		{
			ppNode->_left = subR;
		}
		else
		{
			ppNode->_right = subR;
		}
		subR->_parent = ppNode;
	}
	parent->_bf = subR->_bf = 0;
}

左右旋

左右旋的第一种情况:新增节点在60的左子树,先进行左旋,然后右旋,最后更新平衡因子

左右旋的第二种情况:新增节点在60的右子树,先进行左旋,然后右旋,最后更新平衡因子

左右旋的第三种情况:60作为新增节点,先进行左旋,然后右旋,最后更新平衡因子

左右旋的条件是parent的平衡因子是-2,cur的平衡因子是1

void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;
	//左旋
	RotateL(parent->_left);
	//右旋
	Rotate(parent);
	//更新平衡因子
	if (bf == -1)
	{
		subLR->_bf = 0;
		subL->_bf = 0;
		parent->_bf = 1;
	}
	else if (bf == 1)
	{
		subLR->_bf = 0;
		subL->_bf = -1;
		parent->_bf = 0;
	}
	else
	{
		subLR->_bf = 0;
		subL->_bf = 0;
		parent->_bf = 0;
	}

}

右左旋和左右选差不多,也有三种情况,这里不再画图

右左旋的条件是parent的平衡因子是2,cur的平衡因子是-1

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

	RotateR(subR);
	RotateL(parent);

	if (bf == 1)
	{
		subRL->_bf = 0;
		subR->_bf = 0;
		parent->_bf = -1;
	}
	else if (bf == -1)
	{
		subRL->_bf = 0;
		parent->_bf = 0;
		subR->_bf = 1;
	}
	else
	{
		subRL->_bf = 0;
		parent->_bf = 0;
		subR->_bf = 0;
	}
}

判断AVL树是否平衡

int _Height(Node* root)
{
	if (root == nullptr)
	{
		return 0;
	}
	return max(_Height(root->_left), _Height(root->_right)) + 1;
}
bool _IsBalance(Node* root)
{
	if (root == nullptr)
	{
		return true;
	}
	int LeftHeight = _Height(root->_left);
	int RightHeight = _Height(root->_right);
	//不平衡
	if (abs(LeftHeight - RightHeight) >= 2)
	{
		cout << root->_kv.first << "\n";
		return false;
	}

	//检查平衡因子
	if (RightHeight - LeftHeight != root->_bf)
	{
		cout << root->_kv.first << "\n";
		return false;
	}
	return _IsBalance(root->_left) && _IsBalance(root->_right);
}

AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即O(logN)但是如果要对AVL树做一些结构修改的操

作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时, 有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数

据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值