AVL树的插入与删除

AVL 树要在插入和删除结点后保持平衡,旋转操作必不可少。关键是理解什么时候应该左旋、右旋和双旋。在Youtube上看到一位老师的视频对这个概念讲解得非常清楚,再结合算法书和网络的博文,记录如下。

1.1 AVL 的旋转

一棵AVL树是其每个节点的左子树和右子树的高度差最多为1的二叉查找树(空树高度定义为-1)。AVL树插入和删除时都可能破坏AVL的特性,可以通过对树进行修正来保证特性,修正方法称为旋转。

下面以4个插入操作为例,说明不同旋转对应的场景。

1.1.1 LL-R

插入结点为6,沿着插入路径向上找到第一个不平衡的结点18;从18开始往下看插入方式,16是18的左孩子的左孩子,这种方式称为LL,用来平衡的旋转方式为右旋,即R。

1.1.2 RR-L

插入结点为48,沿着插入路径向上找到第一个不平衡的结点24;从24开始往下看插入方式,48是24的右孩子的右孩子,这种方式称为RR,用来平衡的旋转方式为左旋,即L。

1.1.3 LR-LR

插入的结点是21,沿着插入路径向上找到第一个不平衡的结点28;从28开始往下看插入方式,21是28的左孩子的右孩子,这种方式称为LR, 用来平衡的旋转方式为双旋转,称为LR旋转。LR旋转即先把R路径行的15进行L旋转,再把L路径上的18进行R旋转。注意顺序。

1.1.4 RL-RL

插入的结点是36,沿着插入路径向上找到第一个不平衡的结点24;从24开始往下看插入方式,36是24的右孩子的左孩子,这种方式称为RL, 用来平衡的旋转方式为双旋转,称为RL旋转。RL旋转即先把L路径行的91进行R旋转,再把R路径上的24进行L旋转。注意顺序。

1.1.5 Problem-Solution

LL问题,使用R旋转;

RR问题,使用L旋转;

LR问题,使用LR旋转(再次强调是先把R分支进行L旋转,再把L分支进行R旋转)

RL问题,使用RL旋转(再次强调是先把L分支进行R旋转,再把R分支进行L旋转)

1.2插入

每次插入后判断树是否平衡,有两种方式:一是每个结点存储平衡因子并随时更新;二是每个结点存储当前的高度(叶结点为0,每向上一层高度+1)。据相关资料,“存储平衡因子所得到的些微的速度优势很难抵消清晰度和相对简明性的损失”,实际需要的也只是两棵子树的高度差,所以建议在每个结点上存储树的高度。

  struct AvlNode
{
	ElementType Element;
	AvlTree Left;
	AvlTree Right;
	int Height;
	struct AvlNode()
	{
		Element = -1;
		Left = NULL;
		Right = NULL;
		Height = 0;
	}
};

插入结点时,如果树的高度不变,那么插入完成;如果高度出现不平衡,则根据插入情况适当做单旋或者双旋,更新高度(并解决好与树其他部分的连接),从而完成插入。只需要判断高度差为2的情况,因为一出现不平衡,就立刻调整,所以高度差不会大于2。整个插入过程,就是二叉搜索树的插入+平衡调整。

AvlTree Insert(ElementType x, AvlTree T)
{
	if (T == NULL)
	{
		//创建并返回一棵单结点的树
		T = new AvlNode();
		assert(T);
		T->Element = x;
		return T;
	}
	else if (x < T->Element)
	{
		T->Left = Insert(x, T->Left);
		//调整最深的不平衡的结点
		//高度不平衡时,该结点的两棵子树高度差为2
		if (Height(T->Left) - Height(T->Right) == 2)
		{
			if (x < T->Left->Element)//Problem: LL->Solution:R
				T = SingleRotateR(T);
			else  //Problem: LR->Solution: LR
				T = DoubleRotateLR(T);
		}
	}
	else if (x > T->Element)
	{
		T->Right = Insert(x, T->Right);
		if (Height(T->Right) - Height(T->Left) == 2)
		{
			if (x > T->Right->Element)//Problem: RR->Solution:L
				T = SingleRotateL(T);
			else//Problem: RL->Solution:RL
				T = DoubleRotateRL(T);
		}
	}
	T->Height = Max(Height(T->Left), Height(T->Right)) + 1;
	return T;
}

下面来看看旋转。沿插入点向上的第一个不平衡的点(也是最深的不平衡点),记为α点,则L旋转和R旋转,LR旋转和RL旋转,分别都是关于这一点的镜像对称,因此我们就讨论R旋转和LR旋转。

R旋转:

在子树X中插入结点,假设K2为α点,这是LL问题,所以要进行R旋转。根据上图调整指针:

Position SingleRotateR(Position k2)
{
	Position k1;

	k1 = k2->Left;
	k2->Left = k1->Right;
	k1->Right = k2;

	k2->Height = Max(Height(k2->Left), Height(k2->Right)) + 1;
	k1->Height = Max(Height(k1->Left), Height(k1->Right)) + 1;

	//k1为新的根结点
	return k1;
}

LR旋转:

如上图,假设K2是α点,插入位置是在Y子树中。这里我们把Y子树看做一个由一个根结点和两棵子树构成,如下图:

根据我们在第一节中讲的,LR问题用LR旋转解决,先对R分支进行L旋转,再对L分支进行R旋转:

Position DoubleRotateLR(Position k3)
{
	k3->Left = SingleRotateL(k3->Left);
	return SingleRotateR(k3);
}

1.3 删除

AVL的删除和二叉搜索树的删除很像,多了平衡的操作。 这里贴出伪代码来理解,完整代码实现:https://github.com/yulifromchina/exercise/tree/master/algorithm/tree/avl%20tree。

伪代码大致如下:

Delete(Tree, data)
{
	if(data < Tree->data) Delete(Tree->left, data);balance(Tree);
        else if(data > Tree->data) Delete(Tree->right,data);balance(Tree);
        else
             if(Tree->left && Tree->right)
                    replace Tree->data with successor or precursor;
                    Delete(Tree->right/left, successor/precursor);
                    balance(Tree);
             else
                   if(Tree->left==NULL)
                         Tree = Tree->right;
                   else if(Tree->right==NULL)
                         Tree = Tree->left;
                   free(Tree‘s origin memory)
        update(Tree‘s Height)
}

当待删除结点是叶子结点时,直接删除即可,不用考虑平衡,因为这是递归调用过程,返回上一层叶子结点的父结点时会进行平衡;当待删除结点是只有左子树或右子树时,用该结点的父节点指向左子树或者右子树,再释放该结点;当待删除结点儿女双全时,用前驱或后继赋值,并递归删除前驱或后继。是不是很二叉搜索树很像?另外,每次递归回来都需要平衡一下,这其实就是从插入的结点向上平衡到根结点的过程。

1.4 参考

  1. Youtebu视频:Basics of AVL tree.
  2. 《数据结构与算法分析:C语言描述》
  3. 一步一步写平衡二叉树(AVL树)

(完)

时间: 2024-10-15 04:33:07

AVL树的插入与删除的相关文章

AVL 树的插入、删除、旋转归纳

参考链接: http://blog.csdn.net/gabriel1026/article/details/6311339 之前简单了解过 AVL 树,知道概念但一直没动手实践过.Now AVL 树是二叉搜索树的一种.二叉搜索树的规则就是:每个节点的 left child 都比自己小,right child 都比自己大.而 AVL 的在此之上又加了一个规则:每个节点的 left 深度与 right 深度只差<=1,这样就能充分利用 二叉树的结构,避免出现一个分支走到黑,导致搜索效率变低.如下图

AVL树的插入和删除

一.AVL 树 在计算机科学中,AVL树是最早被发明的自平衡二叉查找树.在AVL树中,任一节点对应的两棵子树的最大高度差为 1,因此它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下的时间复杂度都是 O(log(n)).插入和删除元素的操作则可能需要借由一次或多次树旋转,以实现树的重新平衡. 节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反).带有平衡因子 1.0 或 -1 的节点被认为是平衡的.带有平衡因子 -2 或 2 的节点被认为是不平衡的,并需要重新平衡这个树.平衡因

树的插入、删除、旋转归纳

AVL 树的插入.删除.旋转归纳 参考链接: http://blog.csdn.net/gabriel1026/article/details/6311339 1126号注:先前有一个概念搞混了: 节点的深度 Depth 是指从根节点到当前节点的长度: 节点的高度 Height 是指从当前节点向下,到子孙中所有叶子节点的长度的最大值. 之前简单了解过 AVL 树,知道概念但一直没动手实践过.Now AVL 树是二叉搜索树的一种.二叉搜索树的规则就是:每个节点的 left child 都比自己小,

AVL树的插入操作(旋转)图解

=================================================================== AVL树的概念 在说AVL树的概念之前,我们需要清楚二茬搜索树的概念.对于二叉搜索树,我们知道它可以降低查找速率,但是如果一个二叉搜索树退化成一棵只剩单支的搜索树,此时的查找速率就相当于顺序表中查找元素,效率变低,时间复杂度由原来的O(logN)变为O(N). 此时就有了AVL(高度平衡二叉搜索树),从它的名字就能知道它也是一棵二叉搜索树,只是它在插入元素的时候

B树和B+树的插入、删除图文详解

简介:本文主要介绍了B树和B+树的插入.删除操作.写这篇博客的目的是发现没有相关博客以举例的方式详细介绍B+树的相关操作,由于自身对某些细节也感到很迷惑,通过查阅相关资料,对B+树的操作有所顿悟,写下这篇博客以做记录.由于是自身对B+树的理解,肯定有考虑不周的情况,或者理解错误的地方,请留言指出. 欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. B树 1. B树的定义 B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时

trie树---(插入、删除、查询字符串)

HDU   5687 Problem Description 度熊手上有一本神奇的字典,你可以在它里面做如下三个操作:  1.insert : 往神奇字典中插入一个单词  2.delete: 在神奇字典中删除所有前缀等于给定字符串的单词  3.search: 查询是否在神奇字典中有一个字符串的前缀等于给定的字符串 Input 这里仅有一组测试数据.第一行输入一个正整数N(1≤N≤100000),代表度熊对于字典的操作次数,接下来N行,每行包含两个字符串,中间中用空格隔开.第一个字符串代表了相关的

数据结构--Avl树的创建,插入的递归版本和非递归版本,删除等操作

AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树. 2.带有平衡条件:每个结点的左右子树的高度之差的绝对值最多为1(空树的高度为-1). 也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树). 对Avl树进行相关的操作最重要的是要保持Avl树的平衡条件.即对Avl树进行相关的操作后,要进行相应的旋转操作来恢复Avl树的平衡条件. 对Avl树的插入和删除都可以用递归实现,文中也给出了插入的非递归版本,关键在于要用到栈. 代码如下: #inclu

看数据结构写代码(57) AVL树的删除

上一节 已经说了 AVL树的插入 操作,可是 只有 插入,没有删除,怎么能叫 动态 查找表呢. 呵呵,博主 赶紧 去 研究了一番.下面 是成果: AVL树的删除 大致 分为 两大块: 1. 查找节点 并 删除 2. 保持 删除 后 平衡因子的 影响 1. 首先 找到 这个 节点,如果 节点 不存在,直接 退出 函数 if (*tree == NULL){//没找到 return false; } 2.如果 存在,分为 四种情况:(根 二叉 排序树的 删除 类似) 1.节点 为 叶子 节点,直接

AVL树(平衡二叉查找树)

首先要说AVL树,我们就必须先说二叉查找树,先介绍二叉查找树的一些特性,然后我们再来说平衡树的一些特性,结合这些特性,然后来介绍AVL树. 一.二叉查找树 1.二叉树查找树的相关特征定义 二叉树查找树,又叫二叉搜索树,是一种有顺序有规律的树结构.它可以有以下几个特征来定义它: (1)首先它是一个二叉树,具备二叉树的所有特性,他可以有左右子节点(左右孩子),可以进行插入,删除,遍历等操作: (2)如果根节点有左子树,则左子树上的所有节点的值均小于根节点上的值,如果根节点有右子树,则有字数上的所有节