C++:AVL树

一、AVL树的概念

二叉搜索树在一定程度上可以提高搜索效率,但是当序列是有序时,此时二叉搜索树退化成单链表,搜索效率退化为O(N)。为了解决这个问题科学家引入了AVL树,又称平衡搜索二叉树

AVL简称平衡二叉树。由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树,根据科学家的英文名也称为 AVL 树。它具有如下几个性质:

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

二、AVL树结构定义

template<class K,class V>
struct AVLTreeNode {
	AVLTreeNode(const pair<K, V>& kv=pair<K,V>())
		:_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_kv(kv)
        ,_bf(0)
         {}

    AVLNode<K, V>* _left;
	AVLNode<K, V>* _right;
	AVLNode<K, V>* _parent;
	pair<K, V>_kv;
	int _bf;
};

template<class K, class V>
class AVLTree {
	typedef AVLTreeNode<K, V> Node;
private:
	Node* _root = nullptr;
public:
    //成员函数
    //...
}

三、AVL树节点的插入

AVL树的节点插入可以分为三个步骤:

  1. 新节点的插入
  2. 平衡因子的更新
  3. 通过平衡因子确定平衡性是否被打破,若平衡性被打破,进行旋转

3.1节点的插入

新节点的插入步骤和普通二叉搜索树的插入步骤一样,找到插入的位置,直接插入即可!

bool Insert(const pair<K, V>& kv) {
		if (_root == nullptr) {
			_root = new Node(kv);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur) {
                parent = cur;
			if (cur->_kv.first < kv.first)
				cur = cur->_right;
			else if (cur->_kv.first > kv.first)
				cur = cur->_left;
			else return false;
		}
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
			parent->_right = cur;
		else
			parent->_left = cur;
		cur->_parent = parent;

		//先更新平衡因子
		//...
        //平衡因子更新后,判断是否需要旋转

		return true;//插入成功返回true
	}

 更新平衡因子并判断是否调整

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


	if (parent->_bf == 0)//在插入前为AVL树,实际上就是判断是否改变了以该节点为根节点的树的高度
	{
		break;
		return true;
	}
	else if (parent->_bf == 1 || parent->_bf == -1)//之前_bf==0改变高度,继续向上调整
	{
		cur = parent;
		parent = parent->_parent;
	}
	else if (parent->_bf == 2 || parent->_bf == -2)//违反规则,旋转后高度与插入前相同,结束调整
	{
		if (parent->_bf == 2)
		{
			if (cur->_bf == 1)
				RotateL(parent);//左旋
			else
				RotateRL(parent);//左右双旋
		}
		else
		{
			if (cur->_bf == -1)
				RotateR(parent);//右旋
			else
				RotateLR(parent);//右左双旋
		}
		break;
	}
	else
	{
		assert(false);//在插入前如果是正确的AVL树则不会进入此处
	}

}

四、旋转

AVL树的旋转:

  1. 左单旋
  2. 右单旋
  3. 左右双旋
  4. 右左双旋

4.1左单旋

 左单旋基本步骤

(1)节点的右孩子替代此节点位置

(2)右孩子的左子树变为该节点的右子树

(3)节点本身变为右孩子的左子树

代码如下

	void RotateL(Node* parent)//条件:parent->_bf==2&&cur->_bf==1(在图中cur是parent的右孩子)
	{
        Node* pparent = parent->_parent;
		Node* cur = parent->_right;
		parent->_right = cur->_left;
		cur->_left = parent;
        if (pparent == nullptr)//判断parent是否为根节点
			_root = cur;
		else
		{
			if (pparent->_left == parent)
				pparent->_left = cur;
			else
				pparent->_right = cur;
		}
		cur->_parent = parent->_parent;
		parent->_parent = cur;
		parent->_bf = cur->_bf = 0;//修改平衡因子
	}

4.2右单旋

 和左单旋类似

 右旋的基本步骤:

(1)节点的左孩子代表此节点

(2)节点的左孩子的右子树变为节点的左子树

(3)将此节点作为左孩子节点的右子树。

	void RotateR(Node* parent)//条件:parent->_bf==-2&&cur->_bf==-1(cur为parent左孩子)
	{
		Node* pparent = parent->_parent;
		Node* cur = parent->_left;
		parent->_left = cur->_right;
		cur->_right = parent;
		if (pparent == nullptr)
			_root = cur;
		else
		{
			if (pparent->_left == parent)
				pparent->_left = cur;
			else
				pparent->_right = cur;
		}
		cur->_parent = parent->_parent;
		parent->_parent = cur;
		parent->_bf = cur->_bf = 0;
	}

 4.3左右双旋

在如上情况下无论怎么单旋都不能解决问题 故引入双旋

由于最终四颗子树的位置都固定,更新的平衡因子只取决于subLR的平衡因子,故分为三种情况处理 

 

	void RotateLR(Node* parent)//条件:parent->_bf == -2 && subL->_bf == 1(subL为parent左孩子)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;
		RotateL(subL);
		RotateR(parent);
		if (bf == 1)
		{
			parent->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

4.4右左双旋

右左双旋其实就是左右双旋的一个镜像,搞明白了左右双旋,右左双旋直接画一下图,总结一下三种情况的平衡因子就行了.

	void RotateRL(Node* parent)//条件:parent->_bf == 2 && cur->_bf == -1(cur为parent的右孩子)
	{
		Node* cur = parent->_right;
		Node* curL = cur->_left;
		int bf = curL->_bf;
		RotateR(cur);
		RotateL(parent);
		if (bf == 1)
		{
			parent->_bf = -1;
			cur->_bf = 0;
			curL->_bf = 0;
		}
		else if (bf == 0)
		{
            parent->_bf = 0;
			cur->_bf = 0;
			curL->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			cur->_bf = 1;
			curL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值