为什么使用AVLTree?
对于一般的搜索二叉树而言,在理想的状态下,寻找一个数的复杂度为o(lgn)(这个树为满二叉树),最坏为o(n),如下图所示:
所以我们需要限制它的左右高度,防止它出现不平衡状态。
AVL树的性质
1. 左子树和右子树的高度之差的绝对值不超过1
2. 树中的每个左子树和右子树都是AVL树
3. 每个节点都有一个平衡因子(我用br表示),任一节点的平衡因子是-1,0,1。(每个节点的平衡因子等于右子树的高度减去左子树的高度 )
AVL树的难点在于如何维护AVL树的性质,以及正确的更新节点的平衡因子。我们一般使用旋转的方法来维护。
如果你还对节点之间的旋转不太清楚的话,你可以看看这篇博客:http://blog.csdn.net/fengasdfgh/article/details/52917671
现在我们主要讨论插入时的情况(删除可以看作是插入的逆向,你学会了insert,就可以自己琢磨出erase)。
在这里你还可能对平衡因子的作用没有什么感触,为了引起你的重视,我在这里说明平衡因子的意义。
就如性质3所说的,平衡因子为节点的左右高度差,它可能的值如下:
1 : 说明该节点的右子树比左子树高度高1.
0 : 右子树和左子树一样高。
-1 : 左子树比右子树高1.
2 : 右子树高度比左子树高度高2,这已经违背了性质 1,我们必须 通过旋转来调整这个子树来维持性质。
-2 : 左子树比右子树高2,这同样也违背了性质1.
我们重点讨论新节点插入后怎么更新节点的br(平衡因子)以及如何维护性质1.
首先我们可以推出新节点的br为0.
如果你以为这就结束了,那未免也太简单了。新插入的节点会导致其父节点的左右高度差改变,继而父节点的平衡因子改变。
那么这种改变如果影响到了其祖先节点的左右高度差改变,它是否会影响到其祖先节点那?
我们又如何去判断?
什么时候不会修改?
如果修改了,怎么去更新它的br值?
我用图片说明:
如图中所示:因为d节点的插入,因为d是c的右节点,说明c的右子树高度一定发生了变化,所以c的平衡因子发生了变化。
注意,因为c的平衡因子发生变化,那么对于节点b来说,b的右子树高度一定发生变化,所以它的平衡因子发生变化。
既然b的平衡因子改变了,为什么没有影响到a?因为b的平衡因子变为了0,这说明它的左右子树高度相等,但这对于a来说,它的右子树高度是没有变的,a的左右子树高度都没变,则它的平衡因子不会变。
比如:在d点插入之前各点状况如下:
左子树高度 右子树高度
a 2 3
b 2 1
c 0 0
d点插入之后
左子树高度 右子树高度
a 2 3
b 2 2
c 0 1
这里我们可以总结出规律:
如果一个点的平衡因子发生变化且变为1,-1,那么它一定对它的父节点产生了影响,是0则不然。
可以看出对平衡因子的调节是一个从下向上调整的过程。
void adjust(Node *child)//调整函数
{
Node *father = child->_father;
//从孩子到父亲,向上调整。
while (child != _root)
{
if (father->_left == child)
father->_br -= 1;
else father->_br += 1;
if (0 == father->_br)
break;
else if (1 == father->_br || -1 == father->_br)
{
child = father;
father = father->_father;
}
}
}
如果你认为这就完了,那么再请拉回去看一下平衡因子还有可能的取值,没错我们还没有讨论平衡因子为2,-2时的状况。
平衡因子变为2,-2,这说明这棵树(具体地说是这棵子树,但它会导致这棵树不为AVLTree)不是AVLTree.那么我们就需要来维护这个二叉树了。如图:
类似的,在右子树插入也是一样的。
我们设新插入的节点为child,则它的父亲为为father。
我们可以从上面找到以下规律;
此时说到的br(平衡因子)值都已经是更新过的,我们从新插入的结点开始向上更新,遇到有节点的br值为2,-2时边调用旋转函数,遇到0
终止,遇到1,-1继续向上调节。
while child != root //最多到达根节点
//更新father的br值代码此处省略
//判断father更新后的br值
if father的br(平衡因子值)为2
if father的br为1
进行左旋;
else 进行右左双旋;
else if father的br(平衡因子值)为-2
if father的br为-1
进行右旋;
else 进行左右双旋;
else if father的br(平衡因子值)为1,-1
child = father;
else break;
C++代码如下:
void adjust(Node *child)
{
Node *father = child->_father;
while (child != _root)
{
if (father->_left == child)
father->_br -= 1;
else father->_br += 1;
if (0 == father->_br)
break;
else if (1 == father->_br || -1 == father->_br)
{
child = father;
father = father->_father;
}
else
{
adjust(child, father);
break;
}
}
}
void adjust(Node *father, Node *grandfather)
{
if (2 == grandfather->_br)//进行左旋转
{
if (-1 == father->_br)//进行右左旋转
{
rotate_right_left(father);
//调整相关节点的高度因子
father->_br = grandfather->_br = 0;
}
else
{
rotate_left(father);
father->_br = grandfather->_br = 0;
}
}
else if (-2 == grandfather->_br)//进行右旋转
{
if (1 == father->_br)//进行左右旋转
{
rotate_left_right(father);
//调整相关节点的高度因子
father->_br = grandfather->_br = 0;
}
else
{
rotate_right(father);
father->_br = grandfather->_br = 0;
}
}
}
基本上到这里就可以说AVL树基本完成了,但是这样没问题吗?我们可以测试一下,思路是我们在构造完AVL树后,对每个节点进行遍历,
判断它的左右子树高度差righthigh- lefthigh是否不超过1.并且也判断是否等于该节点的br值。
判断函数如下:
bool Isblance()
{
int height = 0;
return Isblance(_root, height);
}
bool Isblance(Node *root, int& height)
{
if (NULL == root)
{
return true;
height = 0;
}
int leftmax = 0;
//判断左右子树是否平横
if (!Isblance(root->_left, leftmax))
return false;
int rightmax = 0;
if (!Isblance(root->_right, rightmax))
return false;
//判断该节点的平衡因子值是否正确,把所有平衡因子错误的节点打印出来。
if (root->_br != rightmax - leftmax)
{
cout << "don't blance" << ends;
cout << root->_key << endl;
}
//判断该子树是否平横
if (abs(rightmax - leftmax) >= 2)
return false;
height = (max(leftmax, rightmax)) + 1;
return true;
}
测试函数如下:
void test()
{
AVLtree<int, int> a;
vector<int> Test = {4, 2, 6, 1, 3, 5, 15, 7, 16 ,14};
for (auto p : Test)
{
a.insert(p);
}
a.Inorder();//中序遍历
a.Isblance() ? cout << "blance" << endl : cout << "noblance" << endl;
}
结果如下(vs2015):
好吧,这里出现了问题,虽然这棵树现在的结构正确,但它节点的平衡因子已经有了问题,这势必会有隐患。
我们现在需要知道哪里出现问题,加入调试语句,调试情况如下:
void test()
{
AVLtree<int, int> a;
vector<int> Test = {4, 2, 6, 1, 3, 5, 15, 7, 16 ,14};
for (auto p : Test)
{
a.insert(p);
cout << p << endl;
a.Isblance();
cout << "-----------------" << endl;
}
}
可以看到,插入节点14的时侯出现问题,这是为什们那,我们只要把节点14插入的情况画出来既可:
我们可以看出,节点的br值没有更新正确。
在我们的此前的函数里,我们只是简单在旋转后把相关节点的br值变为0,这是有问题的,我们必须进行完善。
我们可以很容易把所有特殊情况推出:
图中的红节点并不是新插入节点,它只是我们这里逻辑上的child节点。
我们和双旋转的图对比一下,就可以发现,这个特殊情况就是child的一个子节点不为NULL。我们把所有情况列举出来,并且得出结果:
设c = child, b = father, a = grandfather
child的br:-1, father的br : 1, grandfather的br:-2
更新后为:child.br = 0, father.br = 0, grandfather.br = 1.
child的br:1, father的br : 1, grandfather的br:-2
更新后为:child.br = 0, father.br = -1, grandfather.br = 0.
child的br:-1, father的br : -1, grandfather的br:2
更新后为:child.br = 0, father.br = 1, grandfather.br = 0.
child的br:-1, father的br : 1, grandfather的br:2
更新后为:child.br = 0, father.br = 0, grandfather.br = -1.
完善后的调整函数为:
//当需要调整时调用adjust的重载函数。
void adjust(Node *father, Node *grandfather)
{
if (2 == grandfather->_br)//进行左旋转
{
if (-1 == father->_br)//进行右左旋转
{
Node *leftchild = father->_left;
rotate_right_left(father);
//调整相关节点的高度因子
if (0 == leftchild->_br)
{
leftchild->_br = father->_br = grandfather->_br = 0;
}
else if (1 == leftchild->_br)
{
leftchild->_br = 0;
father->_br = 0;
grandfather->_br = -1;
}
else
{
leftchild->_br = 0;
father->_br = 1;
grandfather->_br = 0;
}
}
else
{
rotate_left(father);
father->_br = grandfather->_br = 0;
}
}
else if (-2 == grandfather->_br)//进行右旋转
{
if (1 == father->_br)//进行左右旋转
{
Node *leftchild = father->_right;
rotate_left_right(father);
//调整相关节点的高度因子
if (0 == leftchild->_br)
{
leftchild->_br = father->_br = grandfather->_br = 0;
}
else if (1 == leftchild->_br)
{
leftchild->_br = 0;
father->_br = -1;
grandfather->_br = 0;
}
else
{
leftchild->_br = 0;
father->_br = 0;
grandfather->_br = 1;
}
}
else
{
rotate_right(father);
father->_br = grandfather->_br = 0;
}
}
}。
全部代码如下(只实现了插入操作):
#pragma once
#include <iostream>
#include <stack>
#include <algorithm>
using namespace std;
//节点
template<typename K, typename T = int>
struct AVLNode
{
typedef AVLNode<K, T> Node;
AVLNode(const K& key, const T value = T())
:_key(key)
,_value(value)
,_br(0)
,_left(NULL)
,_right(NULL)
,_father(NULL)
{
}
const K _key;
T _value;
int _br;(平衡因子)
Node *_left;
Node *_right;
Node *_father;
};
//AVLTree
template<class K, class T = int>
class AVLtree
{
public:
typedef AVLtree<K, T> tree;
typedef AVLNode<K, T> Node;
AVLtree()
:_root(NULL)
{};
bool insert(const K& key, const T& value = T())
{
Node *p = new Node(key, value);
if (NULL == _root)
{
_root = p;
return true;
}
Node *cur = _root;
Node *father = cur;
while (cur)
{
father = cur;
if (key == cur->_key)
return false;
else if (key > cur->_key)
cur = cur->_right;
else cur = cur->_left;
}
if (father->_key > key)
father->_left = p;
else father->_right = p;
p->_father = father;
//每次插入完成后进行调整
adjust(p);
return true;
}
void middle_display()
{
middle_display(_root);
cout << endl;
}
bool Isblance()
{
int height = 0;
return Isblance(_root, height);
}
protected:
//左旋
void rotate_left(Node *father)
{
Node *grandfather = father->_father;
Node *great_father = grandfather->_father;
grandfather->_right = father->_left;
if (father->_left != NULL)
father->_left->_father = grandfather;
father->_left = grandfather;
grandfather->_father = father;
if (NULL == great_father)
{
_root = father;
}
else
{
if (great_father->_left == grandfather)
great_father->_left = father;
else great_father->_right = father;
}
father->_father = great_father;
}
//右旋
void rotate_right(Node *father)
{
Node *grandfather = father->_father;
Node *great_father = grandfather->_father;
grandfather->_left = father->_right;
if (father->_right != NULL)
father->_right->_father = grandfather;
father->_right = grandfather;
grandfather->_father = father;
if (NULL == great_father)
{
_root = father;
_root->_father = NULL;
}
else
{
if (great_father->_left == grandfather)
great_father->_left = father;
else great_father->_right = father;
}
father->_father = great_father;
}
//左右旋
void rotate_left_right(Node *father)
{
father = father->_right;
rotate_left(father);
rotate_right(father);
}
//右左旋
void rotate_right_left(Node *father)
{
father = father->_left;
rotate_right(father);
rotate_left(father);
}
//调整函数
void adjust(Node *child)
{
Node *father = child->_father;
while (child != _root)
{
if (father->_left == child)
father->_br -= 1;
else father->_br += 1;
if (0 == father->_br)
break;
else if (1 == father->_br || -1 == father->_br)
{
child = father;
father = father->_father;
}
else
{
adjust(child, father);
break;
}
}
}
//adjust的重载函数
void adjust(Node *father, Node *grandfather)
{
if (2 == grandfather->_br)//进行左旋转
{
if (-1 == father->_br)//进行右左旋转
{
Node *leftchild = father->_left;
rotate_right_left(father);
//调整相关节点的高度因子
if (0 == leftchild->_br)
{
leftchild->_br = father->_br = grandfather->_br = 0;
}
else if (1 == leftchild->_br)
{
leftchild->_br = 0;
father->_br = 0;
grandfather->_br = -1;
}
else
{
leftchild->_br = 0;
father->_br = 1;
grandfather->_br = 0;
}
}
else
{
rotate_left(father);
father->_br = grandfather->_br = 0;
}
}
else if (-2 == grandfather->_br)//进行右旋转
{
if (1 == father->_br)//进行左右旋转
{
Node *leftchild = father->_right;
rotate_left_right(father);
//调整相关节点的高度因子
if (0 == leftchild->_br)
{
leftchild->_br = father->_br = grandfather->_br = 0;
}
else if (1 == leftchild->_br)
{
leftchild->_br = 0;
father->_br = -1;
grandfather->_br = 0;
}
else
{
leftchild->_br = 0;
father->_br = 0;
grandfather->_br = 1;
}
}
else
{
rotate_right(father);
father->_br = grandfather->_br = 0;
}
}
}
void Inorder(Node *root)
{
if (NULL == root)
return;
middle_display(root->_left);
cout << root->_key << ends;
Inorder(root->_right);
}
bool Isblance(Node *root, int& height)
{
if (NULL == root)
{
return true;
height = 0;
}
int leftmax = 0;
if (!Isblance(root->_left, leftmax))
return false;
int rightmax = 0;
if (!Isblance(root->_right, rightmax))
return false;
if (root->_br != rightmax - leftmax)
{
cout << "don't blance" << ends;
cout << root->_key << endl;
}
if (abs(rightmax - leftmax) >= 2)
return false;
height = (max(leftmax, rightmax)) + 1;
return true;
}
private:
Node *_root;
};
AVL数虽然在很大程度上保持了平衡,但是它却太难理解,维护代价高。RBTree相比AVLTree则更为简单。