数据结构-----平衡二叉树(Balanced Binary Tree)

AVL树的定义及其特性:

平衡二叉树(Balanced Binary Tree 或 Height-Balanced Tree)又称AVL树。它或者是一颗空树,或者是具有以下性质的二叉树:

  • 它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1;
  • 该树上任意结点的平衡因子BF(Balance Factor)定义为该结点左子树的深度减去右子树的深度,则平衡因子只能取-1、0、1,否则表示该树不平衡。

由上面所提出的性质可知,AVL树实际上只是BST树的延伸。相对与BST树,它对于树的结构提出了更加苛刻的要求。分析容易得知,在添加结点和删除结点的过程中,可能会导致树失去平衡性质,因此还需在删除结点之后重新对树进行排序,保证树的平衡性质。

结构定义:

根据以上描述,AVL树不过是在BST树上根据平衡因子判断进行了一些限制与调整,其树的基本结构并没有改变,依旧是采用二叉链表的方式进行了结构定义。但与BST树不同的是,AVL树需要为每个结点增加一个成员变量height,表示该结点的高度。为了判断增加或删除某个结点是否会导致整颗树的失衡,还需定义两个API接口,分别用来返回某个结点的高度以及该结点左右子树的高度差。

具体实现:

class AVLTree
{
public:
	AVLTree() :_root(nullptr)
	{}
	void insert(const int val);
	void erese(const int val);
	void levelOrder();
private:
	struct AVLNode 
	{
		AVLNode(int data = 10)
			:_data(data)
			,_left(nullptr)
			,_right(nullptr)
			,_height(1)      //单独一个结点自身看来其深度为1
		{}
		int _data;
		AVLNode* _left;
		AVLNode* _right;
		int _height; //表示当前结点的高度
	};
	AVLNode* _root;
	AVLNode* insert(AVLNode* node, const int val);
	AVLNode* erease(AVLNode* node, const int val);
	int height(AVLNode* node);                //返回结点的高度
	int maxHeight(AVLNode* node1, AVLNode* node2); 
	AVLNode* LeftRotate(AVLNode* node);       //左旋操作
	AVLNode* RightRotate(AVLNode* node);      //右旋操作  
	AVLNode* LeftBalance(AVLNode* node);      //左右旋转  
	AVLNode* RightBalance(AVLNode* node);     //右左旋转  
	int AVLTreeLenth(AVLNode* node);
	void levelOrder(AVLNode* node,int lenth)const;
};

int AVLTree::height(AVLNode* node)
{
	return node == nullptr ? 0 : node->_height;
}
int AVLTree::maxHeight(AVLNode* node1, AVLNode* node2)
{
	return height(node1) > height(node2) ? height(node1) : height(node2);
}

关于AVL树因添加结点导致失衡的原因为以下四种情况:

1、在某个结点的左子树中插入一个左孩子导致失衡(LL),采取右旋转策略;

2、在某个结点的右子树中插入一个右孩子导致失衡(RR),采取左旋转策略;

3、在某个结点的左子树中插入一个右孩子导致失衡(LR),采取先左旋后右旋策略;

4、在某个结点的右子树中插入一个左孩子导致失衡(RL),采取先右旋后左旋策略;


以下分别进行介绍:

右旋转:

由于向某节点的左子树中插入一个左孩子导致该结点失衡,则需要使用右旋转来维护AVL树的平衡;

å¨è¿éæå¥å¾çæè¿°

具体情境为:

代码实现:

AVLTree::AVLNode* AVLTree::RightRotate(AVLNode* node)
{
	AVLNode* child = node->_left;
	node->_left = child->_right;
	child->_right = node;

	node->_height = maxHeight(node->_left, node->_right) + 1;
	child->_height = maxHeight(node->_left, node->_right) + 1;
	return child;
}

左旋转:

由于向某节点的右子树中插入一个右孩子导致该结点失衡,则需要使用左旋转来维护AVL树的平衡;

å¨è¿éæå¥å¾çæè¿°

具体情境为:

代码实现:

AVLTree::AVLNode* AVLTree::RightRotate(AVLNode* node)
{
    AVLNode* child = node->_right;
	node->_right = child->_left;
	child->_left = node;

	node->_height = maxHeight(node->_left,node->_right) + 1;
	child->_height = maxHeight(child->_left, child->_right) + 1;
	return child;
}

左右旋转:

由于向某节点的左子树中插入一个右孩子导致该结点失衡,则需要使用左右旋转来维护AVL树的平衡;

具体情境为:

代码实现:

AVLTree::AVLNode* AVLTree::LeftBalance(AVLNode* node)
{
	node->_left = LeftRotate(node->_left);
	return RightRotate(node);
}

右左旋转:

由于向某结点的右子树中插入一个左孩子导致该结点失衡,则需要进行右左旋转来维护AVL树的平衡;

具体情境为:

代码实现:

AVLTree::AVLNode* AVLTree::RightBalance(AVLNode* node)
{
	node->_right = RightRotate(node->_right);
	return LeftRotate(node);
}

在了解了以上知识后,下面来具体说明AVL树的插入以及删除操作:

AVL的插入:

AVL树的插入与二叉排序树(BST)树的插入方法基本类似。其区别在于插入完当前结点时,需判断该结点父节点左右子树的高度情况。如果左右子树的高度差不满足平衡因子,则说明该结点的插入导致了整棵树的失衡。紧接着需要判断具体是什么原因呢导致的失衡,然后根据递归回溯的特点对整棵树进行调整。

具体代码实现:

AVLTree::AVLNode* AVLTree::insert(AVLNode* node, const int val)
{
	if (node == nullptr)
		return new AVLNode(val);

	if (node->_data > val)
	{
		node->_left = insert(node->_left, val);

		if (height(node->_left) - height(node->_right) > 1)
		{
			//说明左子树失衡,此时需要判断是因为左子树左孩子导致的失衡,还是左子树右孩子导致的失衡
			if (height(node->_left->_left) >= height(node->_left->_right))
			{
				//左子树太高,属于LL情况
				node = RightRotate(node);
			}
			else
			{
				//右子树太高,属于LR情况
				node = LeftBalance(node);
			}
		}
	}
	else if (node->_data < val)
	{
		node->_right = insert(node->_right, val);

		if (height(node->_right) - height(node->_left) > 1)
		{
			if (height(node->_right->_right) >= height(node->_right->_left))
			{
				// 右孩子的右子树太高,属于RR情况
				node = LeftRotate(node);
			}
			else
			{
				// 右孩子的左子树太高,属于RL情况
				node = RightBalance(node);
			}
		}
	}
	else
	{
		;
	}

	// 在递归回溯过程中,更新节点的高度值
	node->_height = maxHeight(node->_left, node->_right) + 1;
	return node;
}

AVL的删除:
AVL树结点的删除同样和二叉排序树(BST)基本类似。区别在于删除完一个结点后,需判断是否会引起整棵树的失衡,如果发生失衡,则需要做出相应调整。具体这里不在详细赘述。关于二叉排序树(BST)的相关知识可以参考我的其他博文。

具体实现:

void AVLTree::erese(const int val)
{
	this->_root = erease(_root, val);
}
AVLTree::AVLNode* AVLTree::erease(AVLNode* node, const int val)
{
	if (node == nullptr)
		return nullptr;

	if (node->_data > val)
	{
		node->_left = erease(node->_left, val);

		if (height(node->_right) - height(node->_left) > 1)
		{
			//说明右子树高
			if (height(node->_right->_right) >= height(node->_right->_left))
			{
				//属于RR
				node = LeftRotate(node);
			}
			else
			{
				//属于RL
				node = RightBalance(node);
			}
		}

	}
	else if (node->_data < val)
	{
		node->_right = erease(node->_right, val);

		if (height(node->_left) - height(node->_right) > 1)
		{
			//说明左子树高
			if (height(node->_left->_left) >= height(node->_left->_right))
			{
				//属于LL
				node = RightRotate(node);
			}
			else
			{
				//属于LR
				node = LeftBalance(node);
			}
		}
	}
	else 
	{
		//找到待删除结点的位置,判断情况二
		if (node->_left != nullptr && node->_right != nullptr)
		{
				// 前驱替换
				AVLNode* pre = node->_left;
				while (pre->_right != nullptr)
				{
					pre = pre->_right;
				}
				//将node的值改为前驱结点的值
				node->_data = pre->_data;
				//直接删除前驱结点,前驱结点一定在当前结点的左子树中
				node->_left = erease(node->_left, pre->_data);
				if (height(node->_right) - height(node->_left) > 1)
				{
					//说明右子树高
					if (height(node->_right->_right) >= height(node->_right->_left))
					{
						//属于RR
						node = LeftRotate(node);
					}
					else
					{
						//属于RL
						node = RightBalance(node);
					}
				}
			
		}
		//情况一,只有一个左子树
		else
		{
			if (node->_left != nullptr)
			{
				AVLNode* pcur = node->_left;
				delete node;
				return pcur;
			}
			//情况二,只有一个右子树
			else if (node->_right != nullptr)
			{
				AVLNode* pcur = node->_right;
				delete node;
				return pcur;
			}
			//情况二,为叶子结点
			{
				return nullptr;
			}
		}
	}

	node->_height = maxHeight(node->_left, node->_right) + 1;
	return node;
}

关于AVL树的完整代码,参考以下链接:(其中还有RB树,字典树等数据结构具体实现,感兴趣的可以访问下哦!https://github.com/fdklcl/data-structure-method-achieve

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值