AVL树(Adelsin-Velskii-Landis Tree)是平衡二叉搜索树的一种,它要求任何节点的左右子树的高度相差最多为1。现在将总结AVL树的基本操作。
1.关于树的节点的一些常用术语的定义
a.路径长度(length):根节点至任意节点之间有唯一路径,该路径所经过的边数即为路径长度
b.深度(depth):根节点至任意节点的路径长度即为该节点的深度
c.高度(height):某节点至其最深子节点的路径长度,即为该节点的高度
e.大小(size):节点的所有子节点包括自己在内的节点总数
2.关于树的概念:
a).二叉树的概念:
二叉树的意义:任何节点最多只允许两个子节点。
递归定义二叉树:一个二叉树不为空,便是由一个根节点和左右两个子树构成;左右子树都可能为空
b).二叉搜索树:
二叉搜索树节点的放置规则:任何节点的键值一定大于其左子树中每一个节点的键值,并小于其右子树中每一个节点的键值。
c).平衡二叉搜索树:
二叉搜索树中没有任何一个节点的过深(深度过大)
3.AVL树
基本思想:由于只有“插入点至根节点”路径上的各节点可能改变平衡状态,因此,只要调整“插入点至根节点”路径上,平衡被破坏的各个节点中最深的那个节点,即可使整棵树重新获得平衡。
平衡的意义:任何节点的左右子树的高度差最多为1
如上图所示,其中黄色节点(11)是新插入的节点,该新插入节点将会导致该节点至根节点的路径上的节点的平衡状态被破坏,如上图中,18号节点的平衡即被破坏(左子树高度为3,右子树高度为1,差值大于2),且22号的平衡也被破坏。按照AVL节点的基本处理思想,将调整“插入点至根节点”路径上(红色路径),平衡被破坏的各个节点(18,22)中最深的节点,即为18号节点,(22号节点深度为0,18号节点深度为1)。
因此,在上述例子中,为了插入11号节点,将要调整18号节点。
具体操作:
假设上述选定的最深节点为X,由于节点最多拥有两个子节点,因此可以分为以下四种情况:
1)插入节点位于X的左节点的左子树——左左(上述例子中即为左左)
2)插入节点位于X的左节点的右子树——左右
3)插入节点位于X的右节点的左子树——右左
4)插入节点位于X的右节点的右子树——右右
1)与4)对称,称为外侧插入,可以用单旋转来调整解决
2)与3)对称,称为内侧插入,可以用双旋转来调整解决
左左则右旋:如下
左图为旋转前的左左的情况,其中X为平衡被破坏的最深的节点,将其进行右旋后得到右图,可以看出,右图中的节点满足AVL树平衡的要求。右右的情况类似。
左右则双旋,先旋转为左左,再次使用跟左左相同的处理方式,如下:
如图所示,其中左图为左右的情况,首先左旋插入节点的父节点,将树转为左左的情况,即如中图。然后调整节点X,同样使用右旋,则树将满足AVL的平衡要求。对右左的情况类似。
AVL树即采用上述操作维护平衡,使得任意节点的左右子树的深度不会相差超过1,以保持平衡二叉搜索树的要求
4.AVL树的实现
#include <memory>
#include <queue>
using namespace std;
class Node;
typedef shared_ptr<Node> NodePtr;
extern NodePtr nil;
struct Node
{
int height;
int key;
NodePtr left;
NodePtr right;
NodePtr parent;
Node(int k = 0,int h = 1) :key(k), height(h),left(nil),right(nil),parent(nil){}
};
NodePtr nil(new Node(0, 0));
struct AVLTree
{
NodePtr root;
AVLTree() :root(nil){}
NodePtr insert(int); //找到对应位置并插入
void adjustTree(NodePtr); //插入新的节点后沿树往上不断判断是否以当前顶点为根的子树是否需要调整
void leftRotate(NodePtr);
void rightRotate(NodePtr);
void bfTree(); //广度优先遍历树,查看AVL树是否正确
};
void AVLTree::bfTree()
{
queue<NodePtr> que;
que.push(root);
while (!que.empty())
{
NodePtr q = que.front();
que.pop();
if (q == nil)
{
cout << "nil ";
}
else
{
cout << q->key << " ";
que.push(q->left);
que.push(q->right);
}
}
}
NodePtr AVLTree::insert(int val)
{
shared_ptr<Node> curr(new Node(val));
shared_ptr<Node> y(nil), x(root);
while (x != nil)
{
y = x;
x->height += 1;
if (curr->key < x->key)
x = x->left;
else // curr->key >= x->key
x = x->right;
}
curr->parent = y;
if (y == nil)
{
root = curr;
}
else
{
if (curr->key < y->key)
y->left = curr;
else
y->right = curr;
adjustTree(curr);
}
return curr;
}
void AVLTree::leftRotate(NodePtr x)
{
NodePtr y = x->right;
x->right = y->left;
if (y->left != nil)
y->left->parent = x;
y->parent = x->parent;
if (x->parent == nil)
root = y;
else
{
if (x == x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
}
x->parent = y;
y->left = x;
x->height = max(x->left->height, x->right->height) + 1; //左旋后调整节点的高度
y->height = max(y->left->height, y->right->height) + 1;
}
void AVLTree::rightRotate(NodePtr x)
{
NodePtr y = x->left;
x->left = y->right;
if (y->right != nil)
y->right->parent = x;
y->parent = x->parent;
if (x->parent == nil)
root = y;
else
{
if (x == x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
}
x->parent = y;
y->right = x;
x->height = max(x->left->height, x->right->height) + 1;
y->height = max(y->left->height, y->right->height) + 1;
}
void AVLTree::adjustTree(NodePtr x)
{
if (x->parent == nil || x->parent->parent == nil)
return;
NodePtr pparent = x->parent->parent;
if (pparent != root)
{
x = x->parent;
pparent = pparent->parent;
}
//若树高度差超过2,则当即进行调整
if (abs(pparent->left->height - pparent->right->height) >= 2)
{
//左子树
if (x->parent == pparent->left)
{
if (x == x->parent->right) //左右子树
leftRotate(x->parent);
rightRotate(pparent);
}
else //右子树
{
if (x == x->parent->left) //右左子树
rightRotate(x->parent);
leftRotate(pparent);
}
}
adjustTree(pparent); //沿树往上
return;
}