AVL树的插入与删除,重点是四种旋转

本文不是入门篇,零基础请绕过。

先用维基上的话来介绍一下基本概念。

       在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G.M. Adelson-Velsky和E.M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

       节点的平衡因子是它的左子树的高度减去它的右子树的高度。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

       所谓失去平衡的最小子树,是以距离插入结点最近的、且平衡因子绝对值大于1的结点作为根的子树。我们下面说的旋转,就是要旋转这个最小子树。

       当AVL树不平衡时,有以下四种情况:LL、LR、RR、RL。

针对四种种情况可能导致的不平衡,可以通过旋转使之变平衡。有两种基本的旋转:
(1)左旋转:将根节点旋转到(根节点的)右孩子的左孩子位置
(2)右旋转:将根节点旋转到(根节点的)左孩子的右孩子位置

有人也把左旋转叫做逆时针旋转,右旋转叫做顺时针旋转。

下面结合代码来说一下。

typedef struct Node *NodePtr, *AVL_Tree;

struct Node{
	NodePtr left;
	NodePtr right;
	int height;
	int data;

	Node(int data){
		this->data = data;
		height = 0;
		right = nullptr;
		left = nullptr;
	}
};

其实后来想想,在结点的结构中应该加入指向父结点的指针,这样,就能避免AVL树 用递归来进行插入和删除结点了。也就是类三叉链表结构。

1.  LL情况

采用右旋方法来解决 失衡问题。

《AVL树的插入与删除,重点是四种旋转》

从上图我们看到,最小子树的根,即失衡结点为A,采用右旋,将A旋转到A的左孩子的右孩子位置。可以形象地看出,结点A绕着自己的左孩子B,向右顺时针旋转到了当前位置。

//将根节点旋转到(根节点的)左孩子的右孩子位置
NodePtr RightRotate(AVL_Tree TreeRoot) {
	NodePtr lchild = TreeRoot->left;
	TreeRoot->left = lchild->right;
	lchild->right = TreeRoot;
	TreeRoot->height = Max(TreeRoot->left->height, TreeRoot->right->height);
	lchild->height = Max(lchild->left->height, lchild->right->height);
	return lchild;
}

2.  RR情况

采用左旋转的方法解决。

《AVL树的插入与删除,重点是四种旋转》

从上图可以看出,最小子树的根为A,采用左旋转方法,即向左 逆时针旋转。将根节点旋转到(根节点的)右孩子的左孩子位置。可以形象地看出,结点A绕着自己的右孩子B,向左逆时针旋转到了当前位置。

//将根节点旋转到(根节点的)右孩子的左孩子位置
NodePtr LeftRotate(AVL_Tree TreeRoot) {
	NodePtr rchild = TreeRoot->right;
	TreeRoot->right = rchild->left;
	rchild->left = TreeRoot;
	aTreeRoot->height = Max(aTreeRoot->left->height, TreeRoot->right->height);
	rchild->height = Max(rchild->left->height, rchild->right->height);
	return rchild;
}

3.  LR情况

《AVL树的插入与删除,重点是四种旋转》

LR情况需要两次旋转,先左旋,再右旋。要先 以失衡点的左孩子为根进行左旋,再以失衡点为根进行右旋。

NodePtr LeftRightRotate(AVL_Tree TreeRoot) {
	TreeRoot->left = LeftRotate(TreeRoot->left);
	return RightRotate(TreeRoot);
}

4.  RL情况

《AVL树的插入与删除,重点是四种旋转》

RL情况也需要两次旋转,先右旋,再左旋。要先以失衡点的右孩子为根进行右旋,再以失衡点为根进行左旋。

NodePtr RightLeftRotate(AVL_Tree TreeRoot) {
	TreeRoot->right = RightRotate(TreeRoot->right);
	return LeftRotate(TreeRoot);
}

AVL树的插入与删除

凡是二叉查找树,或者叫做二叉排序树(BST),所有新插入的关键字均存储在新创建的叶子结点上。因为AVL是也是一种特殊的BST,故它的插入的新结点,也都是插入到叶子结点处。再进行旋转调整平衡。

1.插入

//将整型数据newData插入 AVL树 TreeRoot中
NodePtr Insert(int newData, AVL_Tree TreeRoot)
{
	if(TreeRoot == NULL)
	{
		TreeRoot = new Node(newData);
		return TreeRoot;
	}
	else if(newData < TreeRoot->data)
	{
		TreeRoot->left = Insert(newData, TreeRoot->left);
		//已插入完毕,且TreeRoot以下结点已调整完毕
		//因为插入在左子树上,所以如果失衡,则左子树的高度一定大于右子树
		if(TreeRoot->left->height - TreeRoot->right->height == 2)
		{
			if(newData < TreeRoot->left->data)
			{
				//插入在左子树上,且插入的值比左子树的根小
				//所以是LL型失衡
				TreeRoot = RightRotate(TreeRoot);
			} 
			else
			{
				//插入在左子树上,且插入的值比左子树的根大
				//所以是LR型失衡
				TreeRoot = LeftRightRotate(TreeRoot);
			}
		}
	} 
	else
	{
		TreeRoot->right = Insert(newData, TreeRoot->right);
		//已插入完毕,且TreeRoot以下结点已调整完毕
		//因为插入在右子树上,所以如果失衡,则右子树的高度一定大于左子树
		if(TreeRoot->right->height - TreeRoot->left->height == 2)
		{
			if(newData > TreeRoot->right->data)
			{
				//插入在右子树上,且插入的值比左子树的根大
				//所以是RR型失衡
				TreeRoot = LeftRotate(TreeRoot);
			}
			else
			{
				//插入在右子树上,且插入的值比左子树的根小
				//所以是RL型失衡
				TreeRoot = RightLeftRotate(TreeRoot);
			}
		}
	}
	TreeRoot->height = Max(TreeRoot->left->height, TreeRoot->right->height) + 1;
	return TreeRoot;
}

2. 删除操作

把待删除的结点及其所有孩子看做一棵子树,待删除的结点为这棵树的根。

删除操作比较复杂,这里只说一下原理,不写代码了。

1)如果待删除的结点是叶子结点,那么直接删除该叶子结点,并将其父结点对应指针置nullptr,同时,从下向上依次调整该条路径上的平衡。这是最简单的情况

2)当待删除的结点只有一棵子树的时候,将待删除结点的父结点对应的指针指向该子树,之后,删除该结点,同时,从该父结点开始,从下向上依次调整该条路径上的平衡。

3)当待删除的结点有左右两棵子树时,找到该结点的相邻关键字。然后,将该关键字的值赋值给待删除的结点。这样,就把问题转化为删除该关键字所在的叶子结点问题了。即第1)种情况。

解释一下什么叫做相邻关键字。对于不在叶子结点上的关键字a,它的相邻关键字为其左子树中的最大值或 其右子树中的最小值。从该定义可以清晰地看出,相邻关键字一定位于叶子结点处。更直白地说,相邻关键字就是,沿着a的左指针来到其左子树的根结点,然后沿着右指针一下往右走,直到叶子结点为止。  或者是,  沿着a的右指针来到其右子树的根结点,然后沿着左指针一下往左走,直到叶子结点为止。

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