数据结构与算法_AVL平衡二叉树_四种旋转,插入和删除

1 AVL平衡二叉树的概念

平衡二叉树在BST树基础上加了平衡操作。
BST树特点 :在BST树的基础上,引入了节点“平衡”的概念,任意一个节点的左右子树高度差不超过 1 ,为了维持节点的平衡,引入了四种旋转操作,如下:
1.左孩子左子树太高,做右旋转操作
2.右孩子的右子树太高,做左旋转操作
3.左孩子的右子树太高,做左-右旋转操作(也叫左平衡操作)
4.右孩子的左子树太高,做右-左旋转操作(也叫右平衡操作)

AVL树:记忆的时候,与BST树的插入删除联系起来。
下面分别记录AVL的四种失衡操作,并举例说明。

2 AVL的四种旋转操作

2.1 右旋转

右旋转调整。如下图所示,40左子树高度为2,右子树高度为0 ,这种情况称为左失衡,需要进行右旋转。
在这里插入图片描述

2.2 左旋转

左旋转调整。当节点的右子树高度大于节点的左孩子高度时候,如下图情况,需要做左旋转调整。
调整方法:先记录当前节点的右孩子。然后,当前节点的右孩子指向当前节点孩子的左孩子;然后孩子节点的左孩子指向当前节点。
在这里插入图片描述

2.3 左右旋转

左右旋转,也叫左平衡调整。这种情况需要调整两次,先进行一次左旋转,再进行一次右旋转。如下图所示。
在这里插入图片描述

2.4 右左旋转

右左旋转,也称为有平衡操作。这种情况也需要调整两次,即先进行一次右旋转,再进行一次左旋转。
在这里插入图片描述

3 AVL的插入,旋转调整举例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. BST树 和 AVL树插入1-10后,对比

BST树插入1-10后,BST树变成了一个链表;AVL树则是一颗平衡二叉树。
在这里插入图片描述

5. AVL四种旋转操作,插入,删除代码实现

#include <iostream>
#include <functional>
using namespace std;

// 定义AVL节点类型 
template <typename T>
class AVLTree
{
public:
	AVLTree() : root_(nullptr) { }

	// 插入操作
	void insert(const T &val)
	{
		root_ = insert(root_, val);
	}

	// 删除操作
	void remove(const T & val)
	{
		root_ = remove(root_, val);
	}


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_;

	//
	int max(int a, int b)
	{
		return a > b ? a : b;
	}


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

	/* 
	*	功能:node节点进行右旋转,并返回旋转后的根节点
	*
	*	参数:
	*		 node : 当前节点
	*	
	*   返回值: 
	*		 返回旋转后的根节点
	*/
	Node *rightRotate(Node *node)
	{
		Node *child = node->left_;
		node->left_ = child->right_;
		child->right_ = node;

		// 旋转后根节点和child节点发生了变化,所以要更新高度值。
		node->height_ = max(height(node->left_), height(node->right_)) + 1;
		node->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为旋转轴	
	*/		
	Node * leftBalance(Node *node)
	{
		node->left_ = leftRotate(node->left_);  // 先对根节点左孩子进行左旋转
		return rightRotate(node);				// 再对,根节点右孩子进行右旋转。
	}

	/*
	*	功能:右左旋转,并把根节点返回
	*
	*	参数:
	*		  node : 以参数node为旋转轴
	*/
	Node * rightBalance(Node *node)
	{
		node->right_ = rightRotate(node->right_);  // 以左孩子为根进行左旋
		return leftRotate(node);
	}

	/*
	*	功能:node中插入节点,并返回新插入节点
	*
	*/
	Node * insert(Node *node, const T & val)
	{
		if (node == nullptr)
		{
			return new Node(val);
		}

		if (node->data_ > val)   // 向当前节点的左子树中插入新节点,所以要判断,当前节点的左子树是否太高
		{
			node->left_ = insert(node->left_, val);  // 新插入的节点都在叶子节点上,所以下面回溯时,调整树的平衡状态。

			// 在回溯时候,判断节点是否失衡,如果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);

			// 在递归回溯时候,判断节点是否失衡,Node的右子树太高,node 失衡了
			if (height(node->right_) - height(node->left_) > 1)
			{
				if (height(node->right_->right_) >= height(node->right_->left_))  // 1-10,插入节点9时会出现这种情况
				{
					node = leftRotate(node);
				}
				else
				{
					node = rightBalance(node);
				}

			}
		}
		else // 如果新插入节点与当前节点相等,不插入,不用再递归,直接回溯
		{
			;
		}
		
		// 更新节点高度。因为递归中增加了新节点,所以回溯时候,更新节点的高度。
		node->height_ = max(height(node->left_), height(node->right_)) + 1;

		return node; // rrturn后,直接回溯,不再向下递归
	}

	/*
	*	功能:删除val节点
	*
	*	参数:
	*		  node: 当前处理的根节点
	*		  val : 待删除的值
	*/
	Node * remove(Node *node,const T & val)
	{
		if (node == nullptr)
		{
			return nullptr;
		}

		if (node->data_ > val)    // 如果当前节点大于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)
			{
				// 为了避免删除前驱和后继节点造成节点失衡,she高删除谁
				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->right_ != 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;
	}
};


int main()
{
	AVLTree<int> avl;
	for (int i = 1; i < 11; i++)
	{
		avl.insert(i);
	}

	avl.remove(6);
	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值