408-AVL树学习大全

本文详细介绍了AVL树的概念,它是一种二叉平衡搜索树,确保任意节点的左右子树高度差不超过1。为了维持平衡,文章讨论了四种旋转操作:右旋、左旋、左-右旋和右-左旋,以及如何在插入和删除操作中应用这些旋转以保持树的平衡。同时,给出了AVL树节点结构、插入和删除操作的C++实现代码,确保搜索时间复杂度为O(logn)。
摘要由CSDN通过智能技术生成

AVL树又叫
二叉平衡搜索树

是在BST树的基础上增加节点平衡操作
节点平衡:任意节点的左右子树高度差不超过1)(可以是0,1,-1)

在这里插入图片描述
上图也称作BST树,但是搜索的时间复杂度不能达到对数时间了!
已经相当于一个链表了!

AVL树的旋转操作

AVL树为了维护节点平衡引入的四种节点旋转操作

节点失衡的原因是由于:
1、左孩子的左子树太高了
在这里插入图片描述
我们看到:40节点失衡了!不满足AVL树的概念了!
所以我们为了得到一个平衡树(AVL)树,以达到log以2为底的n的时间复杂度
要进行旋转操作
顺时针的右旋转操作!!!
以40为轴,把30转到40这个位置,把40转下来了。
在这里插入图片描述
如果原本30还有一个右孩子,怎么办?
在这里插入图片描述
因为x都是大于30,小于40的(搜索树的性质)
在这里插入图片描述

在这里插入图片描述
左右子树有变的节点要改变高度值,进行更新
node和child的高度值都要更新哦!!!

2、右孩子的右子树太高了:
在这里插入图片描述
40的左右子树高度差超过1,失衡了。
在这里插入图片描述
所以我们现在要做个左旋转操作!
以40为轴,50旋转上去,50的左孩子就是40了,x就是按照大于40小于50的范围

在这里插入图片描述
在这里插入图片描述
然后node和child的高度值都要更新哦!!!

3、左孩子的右子树太高了:
在这里插入图片描述
一次旋转是解决不了问题的!
得做 左 右 旋转! (左平衡操作)
在这里插入图片描述
首先,以child为根节点做一个左旋转,变成
在这里插入图片描述
然后就以40为根节点,做一个右旋转操作
在这里插入图片描述

4、由于右孩子的左子树太高了:
在这里插入图片描述
得 进行 右 左 旋转 (右平衡操作)

首先以child为根节点进行一个右旋转

在这里插入图片描述

然后再以40节点为轴做一个左旋转操作
在这里插入图片描述
我们把这2种情况的节点的高度更新和旋转操作都封装在左右旋转的函数上!!!

AVL树的旋转操作就是以上4种情况了,解决了局部平衡,全局是否平衡,需要向上回溯!

AVL树的代码定义

//AVL树   二叉平衡搜索树
template<typename T>
class AVLTree
{
public:
	//AVL的初始化
	AVLTree() :root_(nullptr) {}
private:
	//定义AVL树节点类型
	struct Node
	{
		Node(T data = T())
			:data_(data)
			, left_(nullptr)
			, right_(nullptr)
			, height_(1)
		{}
		T data_;
		Node* left_;
		Node* right_;
		int height_;//记录节点的高度值
	};
	//指向根节点
	Node* root_;
};

AVL树的节点平衡操作代码实现

//返回节点的高度值
int height(Node* node)
{
	return node == nullptr ? 0 : node->height_;
}

//右旋转操作 以参数node为轴做右旋转操作,并把新的根节点返回
Node* rightRotate(Node* node)
{
	//节点旋转
	Node* child = node->left_;//右旋转是左孩子的左子树太高了 
	node->left_ = child->right_;
	child->right_ = node;
	//高度更新
	node->height_ = max(height(node->left_), height(node->right_)) + 1;
	child->height_ = max(height(child->left_), height(child->right_)) + 1;
	//返回旋转后的子树新的根节点
	return child;
}

//左旋转操作 以参数node为轴做左旋转操作,并把新的根节点返回
Node* leftRotate(Node* node)
{
	//节点旋转
	Node* child = node->right_;//右旋转是右孩子的右子树太高了 
	node->right_ = child->left_;
	child->left_ = node;
	//高度更新
	node->height_ = max(height(node->left_), height(node->right_)) + 1;
	child->height_ = max(height(child->left_), height(child->right_)) + 1;
	//返回旋转后的子树新的根节点
	return child;
}

//左平衡操作 以参数node为轴做左-右旋转操作,并把新的根节点返回
//左孩子的右子树太高了
Node* leftBalance(Node* node)
{
	node->left_ = leftRotate(node->left_);
	return rightRotate(node);
}

//右平衡操作 以参数node为轴做右-左旋转操作,并把新的根节点返回
//右孩子的左子树太高了
Node* rightBalance(Node* node)
{
	node->right_ = rightRotate(node->right_);
	return leftRotate(node);
}

AVL树insert插入代码实现

//AVL树的插入操作实现
Node* insert(Node* node, const T& val)
{
	if (node == nullptr)//递归结束,已经找到插入的位置了
	{
		return new Node(val);//生成节点返回
	}

	if (node->data_ > val)//当前节点值大于要插入的值
	{
		node->left_ = insert(node->left_, val);//向左边插入
		//添加1: 在递归回溯时判断节点是否失衡  node的左子树太高 node失衡了
		if (height(node->left_) - height(node->right_) > 1)
		{
			if (height(node->left_->left_) >= height(node->left_->right_))
			{
				//节点失衡,由于左孩子的左子树太高
				node = rightRotate(node);
			}
			else
			{
				//节点失衡,由于左孩子的右子树太高
				node = leftBalance(node);
			}
		}
	}
	else if (node->data_ < val)//当前节点值小于要插入的值
	{
		node->right_ = insert(node->right_, val);//向右边插入
		//添加2: 在递归回溯时判断节点是否失衡  node的右子树太高 node失衡了
		if (height(node->right_) - height(node->left_) > 1)
		{
			if (height(node->right_->right_) >= height(node->right_->left_))
			{
				//节点失衡,由于右孩子的右子树太高
				node = leftRotate(node);
			}
			else
			{
				//节点失衡,由于右孩子的左子树太高
				node = rightBalance(node);
			}
		}
	}
	else
	{
		; //找到相同节点了,不用再往下递归了,直接向上回溯
	}

	//添加3: 因为子树中增加了新的节点  在递归回溯时检测更新节点高度
	node->height_ = max(height(node->left_), height(node->right_)) + 1;

	return node;
}

AVL树的删除操作

//删除操作实现
Node* remove(Node* node, const T& val)
{
	if (node == nullptr)//没找到要删除的节点
	{
		return nullptr;
	}

	if (node->data_ > val)//当前节点的值大于要删除的节点的值
	{
		node->left_ = remove(node->left_, val);
		//左子树删除节点,可能造成右子树太高
		if (height(node->right_) - height(node->left_) > 1)
		{
			if (height(node->right_->right_) >= height(node->right_->left_))
			{
				//右孩子的右子树太高
				node = leftRotate(node);
			}
			else
			{
				//右孩子的左子树太高
				node = rightBalance(node);
			}
		}
	}
	else if (node->data_ < val)//当前节点的值小于要删除的节点的值
	{
		node->right_ = remove(node->right_, val);
		//右子树删除节点,可能导致左子树太高
		if (height(node->left_) - height(node->right_) > 1)
		{
			if (height(node->left_->left_) >= height(node->left_->right_))
			{
				//左孩子的左子树太高
				node = rightRotate(node);
			}
			else
			{
				//左孩子的右子树太高
				node = leftBalance(node);
			}
		}
	}
	else
	{
		//找到了 先处理有两个孩子的节点删除情况
		if (node->left_ != nullptr && node->right_ != nullptr)
		{
			//为了避免删除前驱或者后继节点造成节点失衡,谁高删除谁
			if (height(node->left_) >= height(node->right_))
			{
				//删前驱节点
				Node* pre = node->left_;
				while (pre->right_ != nullptr)
					pre = pre->right_;
				node->data_ = pre->data_;//覆盖
				node->left_ = remove(node->left_, pre->data_);//直接删前驱节点
			}
			else
			{
				//删后继节点
				Node* post = node->right_;
				while (post->left_ != nullptr)
					post = post->left_;
				node->data_ = post->data_;//覆盖
				node->right_ = remove(node->right_, post->data_);//直接删除后继节点
			}
		}
		else//删除节点,最多有一个孩子
		{
			if (node->left_ != nullptr)
			{
				Node* left = node->left_;
				delete node;
				return left;
			}
			else if (node->right_ != nullptr)
			{
				Node* right = node->right_;
				delete node;
				return right;
			}
			else//删除的是叶子节点,给父亲节点的孩子域返回空
			{
				return nullptr;
			}
		}
	}
	//更新节点高度
	node->height_ = max(height(node->left_), height(node->right_)) + 1;
	return node;//递归回溯过程中,把当前节点给父节点返回
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值