1.AVL树简介
AVL树是一种自平衡的二叉搜索树,一颗典型的二叉搜索树(Binary Search Tree,以下简称BST)应该满足Key(left) < Key(root) < Key(right)。即左子树上所有的键值都小于根节点的键值,根节点的键值小于所有右子树上的键值(左右子树都不为空的话)。也正是因为这种特性,如果对于一般的BST不做任何平衡操作的话,在构建BST的时候左右子树的高度会出现严重不均衡的情况,例如:按照升序(降序)输入序列会造成最极端的不平衡情况。
图1 升序构建的BST 图2 降序构建的BST 图3 平衡的BST
1.1一些概念
为了解决这种情况,平衡的BST概念就应运而生了。其中AVL树是最先发明的自平衡二叉搜索树。自平衡的概念就是在插入、删除的过程中,树会自动平衡自身。在这里我们需要规定一下平衡的概念。一颗平衡二叉树,它的左右子树的高度差不能超过1,这个性质应用到这颗树的任意一个节点都要成立。树的高度的概念:从根节点出发,寻找到离它最远的叶节点所经过的路径,如果一个节点为空,它的高度为-1。高度的概念也可以应用到每一个节点。对于图1,节点1的高度为2,节点2的高度为1,节点3的高度为0.对于图3,节点1和节点3的高度都为0,节点2的高度为1。由此我们又可以引申出节点的平衡因子这个概念。一个节点的平衡因子是:height(x->left)-height(x->right),这里就不再举例细说了。
2.具体操作
2.1左旋、右旋、平衡
AVL树最重要的操作其实说穿了只有两个:左旋和右旋。我们所说的左旋右旋是针对某个节点而言。对于上文图1,将节点1向左旋转就形成了图3,对于图2 ,将节点3进行右旋就形成了图3。当某一个节点的平衡因子大于1时,要将该节点进行右旋;当某一节点的平衡因子小于-1时,要将该节点进行左旋。这里说法不够严谨,具体细节我们到平衡操作时再细说。平衡因子大于1,说明左子树高度至少比右子树高度大2,小于-1,右子树高度比左子树至少大2。具体旋转应该怎么操作呢,我们来看一个例子。
图4 左子树高的不平衡BST
图5 右旋后平衡的BST
对比上面两幅图,我们发现不平衡发生在节点x,它的平衡因子为2,根据上文所说,那么从大方向上而言,它需要进行右旋。右旋过后如图5所示,我们发现,前后两颗树,只有被标记的三个节点的相对位置发生了变化,其他节点相对位置没有变化。用代码来表示就是
/*经由下面的3个步骤,原先x的左节点变为了新树的根节点,原先的x节点变为了新树左节点,原先x左节点的右节点变成了x节点的左孩子,还请好好理解*/
Node* t = x->left;
x->left = t->right;
t->right = x;
右旋操作就是这样,左旋操作很类似,我们以同样的方式来举例说明。
图6 右子树高的不平衡BST(图画的有点问题,x的balance=-2,x->right的balance=-1)
图7 左旋后平衡的BST
同样的,左旋时,相对位置发生变化的也只有被标记出来的3个节点。所以用代码来表示就是
Node* t = x->right;
x->right = t->left;
t->left = x;
和右旋的代码结构一模一样,只不过是对称了一下而已。
理解了左旋和右旋,那我们再来理解平衡就简单