AVL树
定义
一棵 AVL 树或者是空树,或者是具有下列性质的二叉搜索树:它的左子树和右子树都是 AVL 树,且左子树和右子树的高度之差的绝对值不超过1
节点的平衡因子
每个结点附加一个数字,给出该结点右子树的高度减去左子树的高度所得的高度差,这个数字即为结点的平衡因子
b
f
bf
bf。
AVL树任一结点平衡因子只能取 -1, 0, 1。
如果一个结点的平衡因子的绝对值大于1,则这棵二叉搜索树就失去了平衡,不再是AVL树。
如果一棵有 n 个结点的二叉搜索树是高度平衡的,其高度可保持在
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n),平均搜索长度也可保持在
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n)
AVL树的类定义
#include <iostream>
#include “stack.h”
#include <stdlib.h>
template <class E, class K>
struct BSTNode { //二叉树结点类
E data; //数据域
BSTNode<E, K> *left, *right; //左子女和右子女
BSTNode() { left = NULL; right = NULL; }
//构造函数
BSTNode (const E d, BSTNode<E, K> *L = NULL, BSTNode<E, K> *R = NULL)
{ data = d; left = L; right = R;}
//构造函数
~BSTNode() {} //析构函数
void setData (E d) { data = d; } //修改
E getData() { return data; } //提取
bool operator < (const E& x) //重载:判小于
{ return data.key < x.key; }
bool operator > (const E& x) //重载:判大于
{ return data.key > x.key; }
bool operator == (const E& x) //重载:判等于
{ return data.key == x.key; }
};
template <class E, class K>
struct AVLNode : public BSTNode<E, K> {
//AVL树结点的类定义
int bf;
AVLNode() { left = NULL; right = NULL; bf = 0; }
AVLNode (E d, AVLNode<E, K> *l = NULL, AVLNode<E, K> *r = NULL)
{ data = d; left = l; right = r; bf = 0; }
};
template <class E, class K>
class AVLTree : public BST<E, K> {
//平衡的二叉搜索树(AVL)类定义
public:
AVLTree() { root = NULL; } //构造函数
AVLTree (K Ref) { RefValue = Ref; root = NULL; }
//构造函数:构造非空AVL树
int Height() const; //高度
AVLNode<E, K>* Search (K x, AVLNode<E, K> *& par) const;//搜索
bool Insert (E& e1) { return Insert (root, e1); } //插入
bool Remove (K x, E& e1){ return Remove (root, x, e1); } //删除
friend istream& operator >> (istream& in, AVLTree<E, K>& Tree);
//重载:输入
friend ostream& operator << (ostream& out,const AVLTree<E, K>& Tree);
//重载:输出
protected:
int Height (AVLNode<E, K> *ptr) const;
bool Insert (AVLNode<E, K>*& ptr, E& e1);
bool Remove (AVLNode<E, K>*& ptr, K x, E& e1);
void RotateL (AVLNode<E, K>*& ptr); //左单旋
void RotateR (AVLNode<E, K>*& ptr); //右单旋
void RotateLR (AVLNode<E, K>*& ptr);//先左后右双旋
void RotateRL (AVLNode<E, K>*& ptr);//先右后左双旋
};
平衡化旋转
如果在一棵平衡的二叉搜索树中插入一个新结点,造成了不平衡。此时必须调整树的结构,使之平衡化。
平衡化旋转有两类:
单旋转 (左旋和右旋)
双旋转 (左平衡和右平衡)
每插入一个新结点时, AVL 树中相关结点的平衡状态会发生改变。因此, 在插入一 个新结点后,需要从插入位置沿通向根的路径回溯,检查各结点的平衡因子。如果在某一结点发现高度不平衡,停止回溯。从发生不平衡的结点起,沿刚才回溯的路径取直接下两层的结点。
如果这三个结点处于一条直线上,则采用单旋转进行平衡化。单旋转可按其方向分为左单旋转和右单旋转, 其中一个是另一 个的镜像,其方向与不平衡的形状相关。
如果这三个结点处于一条折线上,则采用双旋转进行平衡化。双旋转分为先左后右和先右后左两类。
左单旋转
在结点A的右子女的右子树E中插入新结点,该子树高度增1导致结点A的平衡因子变成2,出现不平衡。为使树恢复平衡,从A沿插入路径连续取3个结点A、C和E,以结点C为旋转轴,让结点A反时针旋转。
图解
代码实现
template <class E, class K>
void AVLTree<E, K>::RotateL (AVLNode<E, K> *& ptr) {
//右子树比左子树高: 做左单旋转后新根在ptr
AVLNode<E, K> *subL = ptr;
ptr = subL->right;
subL->right = ptr->left;
ptr->left = subL;
ptr->bf = subL->bf = 0;
};
右单旋转
在结点A的左子女的左子树D上插入新结点使其高度增1导致结点A的平衡因子增到-2,造成不平衡。为使树恢复平衡,从A沿插入路径插入路径连续取3 个结点A、B和D,以结点B为旋转轴,将结点A顺时针旋转。
图解
代码实现
template <class E, class K>
void AVLTree<E, K>::
RotateR (AVLNode<E, K> *& ptr) {
//左子树比右子树高, 旋转后新根在ptr
AVLNode<E, K> *subR = ptr; //要右旋转的结点
ptr = subR->left;
subR->left = ptr->right; //转移ptr右边负载
ptr->right = subR; //ptr成为新根
ptr->bf = subR->bf = 0;
};
先左后右双旋转
在结点A的左子女的右子树中插入新结点,该子树高度增1导致结点A的平衡因子变为-2,造成不平衡。
图解
以结点E为旋转轴,将结点B反时针旋转,以E代替原来B的位置。
再以结点E为旋转轴,将结点A顺时针旋转。使之平衡化
代码实现
template <class E, class K>
void AVLTree<E, K>:: RotateLR (AVLNode<E, K> *& ptr) {//ptr为要旋转的节点
AVLNode<E, K> *subR = ptr;//结束时的右子树就是要旋转的ptr
AVLNode<E, K> *subL = subR->left;//结束时的左子树就是开始时ptr的左子树
//先左单旋转
ptr = subL->right;//直接将目标节点翻到根节点的位置
subL->right = ptr->left;
ptr->left = subL;
if (ptr->bf <= 0) subL->bf = 0;//调整平衡因子
else subL->bf = -1;
//再右单旋转
subR->left = ptr->right;
ptr->right = subR;
if (ptr->bf == -1) subR->bf = 1;//调整平衡因子
else subR->bf = 0;
ptr->bf = 0;
};
先右后左双旋转
在结点A的右子女的左子树中插入新结点,该子树高度增1。结点A的平衡因子变为2,发生了不平衡。
图解
首先以结点D为旋转轴,将结点C顺时针旋转,以D代替原来C的位置。
再以结点D为旋转轴,将结点A反时针旋转, 恢复树的平衡。
代码实现
template <class E, class K>
void AVLTree<E, K>::
RotateRL (AVLNode<E, K> *& ptr) {
AVLNode<E, K> *subL = ptr;
AVLNode<E, K> *subR = subL->right;
//先右单旋转
ptr = subR->left;
subR->left = ptr->right;
ptr->right = subR;
if (ptr->bf >= 0) subR->bf = 0;
else subR->bf = 1;
//再左单旋转
subL->right = ptr->left;
ptr->left = subL;
if (ptr->bf == 1) subL->bf = -1;
else subL->bf = 0;
ptr->bf = 0;
};