平衡搜索树---AVL树

AVL树是一种高度平衡的二叉搜索树,它的每个结点都有一个平衡因子,这个平衡因子的取值是-1,0,1平衡因子 = 右子树高度 – 左子树高度

AVL树具有的性质:

1.左子树和右子树的高度差不超过1;

2.树中的各子树都是AVL树

一、AVL树结点的定义

为了判断每个子树是否平衡,因此在定义AVL树的结点的时候就应该在搜索二叉树的基础上加一个平衡因子_bf

template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode(const K& key,const V& value)
		:_left(NULL)
		,_right(NULL)
		,_parent(NULL)
		,_key(key)
		,_value(value)
		,_bf(0)
	{}

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

	int _bf;	//平衡因子
};

二、AVL树的操作—插入

①当插入结点的key与某个结点的_key相等时,插入失败,返回false;

每插入一个结点,就应该检查是否平衡,判断的依据就是_bf的值不超过1

a.当插入一个结点后,父亲结点的_bf由-1/1变为0,说明在插入之前父亲节点有一个子树,此时整棵树的高度不变

《平衡搜索树---AVL树》

b.当插入一个结点后,父亲结点的_bf由0变为-1/1,说明在插入之前父亲节点没有子树,此时整棵树的高度一定变

《平衡搜索树---AVL树》

如上图所示的这种情况插入一个结点后整棵树仍然保持平衡的,但如果此时以cur作为父亲节点,插入一个结点后还会平衡嘛?!

《平衡搜索树---AVL树》

此时的ppNode的平衡因子已经变为-2,显然已经不满足AVL树的性质。为了解决这种情况,就要对树进行旋转,以达到平衡。

(PS:我对旋转这块的内容写了一篇博客,在这里就不写了,详情请戳这里>>平衡搜索树中的左单旋&右单旋&双旋

插入的几种情况总结起来就是:

当parent->_bf变为0,就停止更新(平衡因子);当parent->_bf变为-1/1,继续向上更新平衡因子;当parent->_bf变为-2/2,进行旋转

bool Insert(const K& key, const V& value)
	{
		if (_root == NULL)
		{
			_root = new Node(key,value);
			return true;
		}
		Node* cur = _root;
		Node* parent = NULL;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if(cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				//已经有key,插入失败
				return false;
			}
		}
		cur = new Node(key,value);
		cur->_parent = parent;
		if (cur->_key < parent->_key)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		//插入新的结点后,检查是否满足平衡二叉树,不满足就进行旋转
		while(parent)
		{
			if (parent->_left == cur)	//新增结点在左,_bf--
			{
				parent->_bf --;
			}
			else if(parent->_right == cur)	//新增结点在右,_bf++
			{
				parent->_bf ++;
			}
			//判断父亲的平衡因子,若parent->_bf为2、-2,就进行旋转
			if (parent->_bf == 0)
			{
				break;	//parent的平衡因子为0,说明这棵树一定满足平衡二叉树
			}
			else if (parent->_bf == -1 || parent->_bf == 1)
			{
				//继续更新平衡因子
				cur = parent;
				parent = cur->_parent;
			}
			else	//parent->_bf == -2 / 2
			{
				if (parent->_bf == -2)
				{
					if(cur->_bf == -1)
						RotateRight(parent);
					else
						RotateLR(parent);
				}
				else
				{
					if (cur->_bf == -1)
						RotateRL(parent);
					else
						RotateLeft(parent);
				}
			}
		}
		return true;
	}

旋转:

	void RotateLeft(Node* parent)	//左单旋
	{		
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;	//先改变parent的右指针
		if (subRL)	//subRL可能为NULL
		{
			subRL->_parent = parent;
		}

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

		if (ppNode == NULL)
		{
			_root = subR;
			subR->_parent = NULL;
		}
		else
		{
			//判断subR应链接在ppNode的左子树还是右子树
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else
				ppNode->_right = subR;

			subR->_parent = ppNode;
		}

		subR->_bf = parent->_bf = 0;
	}
	void RotateRight(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 == NULL)	//说明parent结点为根节点
		{
			_root = subL;
			subL->_parent = NULL;
		}
		else
		{
			//如果parent不为根节点,判断其在上一个结点的右还是左
			if (ppNode->_left == parent)
				ppNode->_left = subL;
			else
				ppNode->_right = subL;

			subL->_parent = ppNode;
		}
		
		subL->_bf = parent->_bf = 0;
	}
	void RotateLR(Node* parent)		//左右双旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

		RotateLeft(parent->_left);
		RotateRight(parent);

		if (bf == 0)		//subLR本身就是新增节点
		{
			parent->_bf = subL->_bf = subLR->_bf = 0;
		}
		else if(bf == 1)	//subLR的右子树是新增节点
		{
			parent->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else	//bf == -1----subLR的左子树是新增结点
		{
			parent->_bf = 1;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
	}
	void RotateRL(Node* parent)		//右左双旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateRight(parent->_right);
		RotateLeft(parent);

		if (bf == 0)		//subRL本身就是新增节点
		{
			parent->_bf = subR->_bf = subRL->_bf = 0;
		}
		else if(bf == 1)	//subRL的右子树是新增节点
		{
			parent->_bf = -1;
			subR->_bf = 0;
			subRL->_bf = 1;
		}
		else	//bf == -1----subR L的左子树是新增结点
		{
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = -1;
		}
	}

三、AVL树的效率

假设一棵AVL树有N个结点,那么它的高度可以保持在lgN(log以2为底的N的对数),

插入时最好的情况就是第一次就能找到这个结点的位置进行插入,时间复杂度为O(1);最坏的情况是需要遍历整棵树的高度次,时间复杂度为O(lgN)


    原文作者:AVL树
    原文地址: https://blog.csdn.net/qq_33951180/article/details/52970865
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞