AVL定义:一棵空二叉树是AVL树;如果T是非空二叉树,TL和TR分别是其左子树和右子树,则当且仅当TL和TR都为AVL树且|HL-HR|<=1时,T是AVL树。
由定义知道一个AVL树的任何节点的左右子树的高度之差不超过1,这是AVL树最基本的特征。
AVL树节点的平衡因子:节点的平衡因子是它的右子树的高度减去它的左子树的高度。
带有平衡因子 1、0 或 -1 的节点被认为是平衡的。
带有平衡因子 -2 或 2 的节点被认为是不平衡的,并需要重新平衡这个树。
平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。
AVL树节点的平衡因子定义为其左子树的高度减去右子树的高度,我们可以在插入和删除操作的时候更新平衡因子。
一棵AVL树的各节点平衡因子为1,-1, 0
AVL树的高度和节点数:(固定节点数计算最大高度)
高度为 h 的 AVL 树,节点数 N 最多2^h − 1;最少节点数N_(h)=F(h+ 2) - 1 , (F(h+ 2)是 Fibonacci polynomial 的第h+2个数)。
记N_(h)为一棵高度为h的AVL树具有的最小节点数,则最坏情况是它的左右子树的高度不等,
一个是N_(h-1)和N_(h-2),从而得到
N_h=N_(h-1)+N_(h-2)+1 N_0=0,N_1=1
这类似于Fibonacci数列:F_(n)=F_(n-1)+F_(n-2),(F_0=0,F_1=1)
而F_(h)=(1+sqrt(5))^h/sqrt(5)
从而高度h和节点数是对数关系,因此 h=O(log(N_h))
由此容易知道在不考虑恢复AVL树的前提下,它的插入,删除和查找的工作量不超过O(n)。
操作
AVL树的基本操作一般涉及运做同在不平衡的二叉查找树所运做的同样的算法。但是要进行预先或随后做一次或多次所谓的"AVL 旋转"。
假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点的指针为a(即a是离插入点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行进行的规律可归纳为下列四种情况:
1. 单向右旋平衡处理RR:由于在*a的左子树根结点的左子树上插入结点,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行一次右旋转操作;
2. 单向左旋平衡处理LL:由于在*a的右子树根结点的右子树上插入结点,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行一次左旋转操作;
3. 双向旋转(先左后右)平衡处理LR:由于在*a的左子树根结点的右子树上插入结点,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作。
4. 双向旋转(先右后左)平衡处理RL:由于在*a的右子树根结点的左子树上插入结点,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行两次旋转(先右旋后左旋)操作。
插入
向AVL树插入可以通过如同它是未平衡的二叉查找树一样把给定的值插入树中,接着自底向上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有 1.5 乘 log n 个节点,而每次 AVL 旋转都耗费恒定的时间,插入处理在整体上耗费 O(log n) 时间。
在平衡的二叉排序树Balanced BST上插入一个新的数据元素e的递归算法可描述如下:
- 若BBST为空树,则插入一个数据元素为e的新结点作为BBST的根结点,树的深度增1;
- 若e的关键字和BBST的根结点的关键字相等,则不进行;
- 若e的关键字小于BBST的根结点的关键字,而且在BBST的左子树中不存在和e有相同关键字的结点,则将e插入在BBST的左子树上,并且当插入之后的左子树深度增加(+1)时,分别就下列不同情况处理之:
- BBST的根结点的平衡因子为-1(右子树的深度大于左子树的深度,则将根结点的平衡因子更改为0,BBST的深度不变;
- BBST的根结点的平衡因子为0(左、右子树的深度相等):则将根结点的平衡因子更改为1,BBST的深度增1;
- BBST的根结点的平衡因子为1(左子树的深度大于右子树的深度):则若BBST的左子树根结点的平衡因子为1:则需进行单向右旋平衡处理,并且在右旋处理之后,将根结点和其右子树根结点的平衡因子更改为0,树的深度不变;
- 若e的关键字大于BBST的根结点的关键字,而且在BBST的右子树中不存在和e有相同关键字的结点,则将e插入在BBST的右子树上,并且当插入之后的右子树深度增加(+1)时,分别就不同情况处理之。
删除
从AVL树中删除可以通过把要删除的节点向下旋转成一个叶子节点,接着直接剪除这个叶子节点来完成。因为在旋转成叶子节点期间最多有 log n个节点被旋转,而每次 AVL 旋转耗费恒定的时间,删除处理在整体上耗费 O(log n) 时间。(感觉这样处理之后,有可能这个树就不一定还是AVL树了,正确应该在删除的时候也像插入的时候一样,需要旋转调整的吧)
查找
在AVL树中查找同在一般BST完全一样的进行,所以耗费 O(log n) 时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查询而改变。(这是与伸展树查找相对立的,它会因为查找而变更树结构。)