AVL树(高度平衡的二叉搜索树),它可以保持二叉树的高度平衡,尽量降低二叉树的高度,减少树的平均搜索长度
AVL树的性质
1,左子树和右子树的高度之差的绝对值不超过1
2,树中的每个左子树和右子树都是AVL树
3,每个结点都有一个平衡因子,任意一个结点的平衡因子是-1,0,1(每个结点的平衡因子等于右子树的高度减去左子树的高度)
AVL树的效率:一颗AVL树有N个结点,它的高度可以保持在lgN,其中插入删除查找的时间复杂度也为lgN(需要注意的是lg在这里表示的是以2为底)
首先我们定义AVLTreeNode的结点
在这里我们定义的是一个三叉链,作用后面会用到
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K,V>* _left;
AVLTreeNode<K,V>* _right;
AVLTreeNode<K,V>* _parent;
V _value;
K _key;
int _bf;
AVLTreeNode(const K& key,const V& value)
:_left(NULL)
, _right(NULL)
, _parent(NULL)
, _key(key)
, _bf(0)
, _value(value)
{}
};
我们先看一下插入的四种旋转的方式:
1,左单旋
我们在图中可以看出来,parent的平衡因子是2所以我们需要对它进行旋转,我们将subRL给parent的_right,将parent给subR的_left,这样就旋转好了吗?
当然没有我们并不知道原来的parent是根节点还是一颗子树,所以我们要在parent给subR的_left之前要定义一个变量parentparent保存parent 的_parent,如果parentparent为空,那么就说明之前parent是根节点,那么旋转结束之后subR就是旋转结束后这棵树的根节点,如果parentparent不为空,那么就说明这棵树只是一颗子树,如果parent在parentparent的_left那么就把subR给parentparent的_left,反之将subR给parentparent的_right。(这里要记住这是三叉链,我们要把每个都链起来)
代码实现:
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
Node* parentparent = parent->_parent;
parent->_parent = subR;
if (parentparent == NULL)
{
_root = subR;
subR->_parent = NULL;
}
else
{
if (parentparent->_left == parent)
{
parentparent->_left = subR;
subR->_parent = parentparent;
}
else
{
parentparent->_right = subR;
subR->_parent = parentparent;
}
}
subR->_bf = parent->_bf = 0;
}
2,右单旋
思想和左单旋相同,如果理解了左单旋,右单旋自然也就理解了
代码实现:
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
Node* parentparent = parent->_parent;
parent->_parent = subL;
if (parentparent == NULL)
{
_root = subL;
subL->_parent = NULL;
}
else
{
if (parentparent->_left == parent)
{
parentparent->_left = subL;
subL->_parent = parentparent;
}
else
{
parentparent->_right = subL;
subL->_parent = parentparent;
}
}
subL->_bf = parent->_bf = 0;
}
3,左右双旋
我们从图中可以看到,左右双旋进行了一次左单旋,进行了一次右单旋
那么它的平衡因子是怎么调整的呢,
我们把平衡因子的调整分为三种情况
1,subLR没有左孩子
2,subLR没有右孩子
3,subLR有左孩子和右孩子或者左右孩子都没有
代码实现
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = parent->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 1)//subLR没有左孩子
{
parent->_bf = 0;
subL->_bf = -1;
}
else if (bf == -1)//subLR没有右孩子
{
subL->_bf = 0;
parent->_bf = 1;
}
else//subLR有左孩子和右孩子或者左右孩子都没有
{
subL->_bf = parent->_bf = 0;
}
subLR->_bf = 0;
}
4,右左双旋
右左双旋跟左右双旋的算法思想相同,如果理解了上一个,这个可以自己画图理解一下
代码实现:
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)//subRL没有左孩子
{
subR->_bf = 0;
parent->_bf = -1;
subRL->_bf = 0;
}
else if (bf == -1)subRL没有右孩子
{
subR->_bf = 0;
parent->_bf = 1;
subRL->_bf = 0;
}
else//subRL有左孩子和右孩子或者左右孩子都没有
{
parent->_bf = subRL->_bf = subR->_bf = 0;
}
}
插入的实现:
思想:
1,先遍历这棵树,查找要插入的地方
2,new一个新的结点,并连接起来
3,调节平衡因子:
我们在左边添加结点,该结点的父亲的平衡因子要减1
在右边添加结点,该结点的父亲的平衡因子要加1;
那么我们怎么样看插入就结点是否影响祖父呢,我们在这里可以找到平衡因子的规律:如果该结点的父亲的平衡因子从0变到了1或者-1,那么一定会影响祖父,如果该结点的父亲的平衡因子变为了0,那么该结点的插入只会影响父亲的平衡因子。
如果结点的平衡因子变为了2或者-2,那么该AVL树需要旋转,怎么判断该树是哪种旋转呢?我们在这里也可以通过平衡因子判断。
我们通过这张图parent为+2并且他的右孩子(subR)为正那么就是左单旋,如果它的右孩子(subR)为负,那么就是右左双旋。
如果parent为-2并且它的左孩子(subL)为负,那么它就是右单旋
如果它的左孩子(subL)为正,那么它就是左右双旋。
总结一下就是,同号是单旋,异号是双旋。
bool Insert(const K& key,const V& value)
{
if (_root == NULL)
{
_root = new Node(key,value);
return true;
}
Node* cur = _root;
Node* parent = NULL;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//添加结点
cur = new Node(key,value);
if (parent->_key < key)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
//平衡因子
while (parent)
{
if (cur == parent->_left)
{
--(parent->_bf);
}
else
{
++(parent->_bf);
}
if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = cur->_parent;
}
else
{
//2或-2,就要旋转
if (parent->_bf == -2)
{
if (cur->_bf == -1)
{
RotateR(parent);
}
else
{
RotateLR(parent);
}
}
else
{
if (cur->_bf == 1)
{
RotateL(parent);
}
else
{
RotateRL(parent);
}
}
break;
}
}
return true;
}
我们插入完成了,那么如何让判断这棵树是否是AVL树呢?
在这里我们写一个函数来实现:
算法思想:
判断是否是AVL树,我们必须要让这棵树满足AVL树的性质,即左子树和右子树的高度差的绝对值不能超过1,并且它的左子树和右子树也是AVL树。
我们首先实现一个函数求树的高度,然后让每棵树的右子树减去它的左子树。判断他们高度差的绝对值是否是不超过1.
int Depth()
{
return _Depth(_root);
}
int _Depth(Node* root)
{
if (root == NULL)
{
return 0;
}
int left = _Depth(root->_left);
int right = _Depth(root->_right);
return left > right ? left + 1 : right + 1;
}
bool IsBalance()
{
return _IsBalance(_root);
}
bool _IsBalance(Node* root)//时间复杂度为N*N
{
if (root == NULL)
{
return 0;
}
int left = _Depth(root->_left);
int right = _Depth(root->_right);
return abs(right - left) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
我们可以看到用递归实现判断是否是AVL树它的时间复杂度为N*N。
他把这棵树在求高度的时候遍历了一遍,递归的时候还在遍历,这样就使得该函数遍历了两遍,那么我们能否将这棵树的时间复杂度优化到O(N),我们可以想到斐波那契树时,我们倒着往上遍历,时间复杂度会低很多,那么我们的AVL树也可以先找到最左结点,然后倒着向上判断
,把每一层判断的高度的结果返回给上一层,所以我们的depth要加上引用。
代码实现:
//优化到O(N),把高度返回给上一层
bool _IsBalance(Node* root, int& depth)
{
if (root == NULL)//先判断是否为空
{
depth = 0;
return true;
}
int leftDepth = 0;
if (_IsBalance(root->_left, leftDepth) == false)//如果它不平衡直接return false
return false;
int rightDepth = 0;
if (_IsBalance(root->_right, rightDepth) == false)
return false;
if ((rightDepth - leftDepth) != root->_bf)
{
cout << "bf 异常" << root->_key << endl;
//return false;
}
depth = rightDepth > leftDepth ? rightDepth + 1 : leftDepth + 1;
return abs(rightDepth - leftDepth) < 2;
}
完整代码:
#include<iostream>
using namespace std;
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K,V>* _left;
AVLTreeNode<K,V>* _right;
AVLTreeNode<K,V>* _parent;
V _value;
K _key;
int _bf;
AVLTreeNode(const K& key,const V& value)
:_left(NULL)
, _right(NULL)
, _parent(NULL)
, _key(key)
, _bf(0)
, _value(value)
{}
};
template<class K,class V>
class AVLTree
{
typedef AVLTreeNode<K,V> Node;
public:
AVLTree()
:_root(NULL)
{}
bool Insert(const K& key,const V& value)
{
if (_root == NULL)
{
_root = new Node(key,value);
return true;
}
Node* cur = _root;
Node* parent = NULL;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//添加结点
cur = new Node(key,value);
if (parent->_key < key)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
//平衡因子
while (parent)
{
if (cur == parent->_left)
{
--(parent->_bf);
}
else
{
++(parent->_bf);
}
if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = cur->_parent;
}
else
{
//2或-2,就要旋转
if (parent->_bf == -2)
{
if (cur->_bf == -1)
{
RotateR(parent);
}
else
{
RotateLR(parent);
}
}
else
{
if (cur->_bf == 1)
{
RotateL(parent);
}
else
{
RotateRL(parent);
}
}
break;
}
}
return true;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
Node* parentparent = parent->_parent;
parent->_parent = subR;
if (parentparent == NULL)
{
_root = subR;
subR->_parent = NULL;
}
else
{
if (parentparent->_left == parent)
{
parentparent->_left = subR;
subR->_parent = parentparent;
}
else
{
parentparent->_right = subR;
subR->_parent = parentparent;
}
}
subR->_bf = parent->_bf = 0;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
Node* parentparent = parent->_parent;
parent->_parent = subL;
if (parentparent == NULL)
{
_root = subL;
subL->_parent = NULL;
}
else
{
if (parentparent->_left == parent)
{
parentparent->_left = subL;
subL->_parent = parentparent;
}
else
{
parentparent->_right = subL;
subL->_parent = parentparent;
}
}
subL->_bf = parent->_bf = 0;
}
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
subRL->_bf = 0;
}
else if (bf == -1)
{
subR->_bf = 0;
parent->_bf = 1;
subRL->_bf = 0;
}
else
{
parent->_bf = subRL->_bf = subR->_bf = 0;
}
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = parent->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
}
else if (bf == -1)
{
subL->_bf = 0;
parent->_bf = 1;
}
else
{
subL->_bf = parent->_bf = 0;
}
subLR->_bf = 0;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root)
{
if (root == NULL)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
int Depth()
{
return _Depth(_root);
}
int _Depth(Node* root)
{
if (root == NULL)
{
return 0;
}
int left = _Depth(root->_left);
int right = _Depth(root->_right);
return left > right ? left + 1 : right + 1;
}
//bool IsBalance()
//{
// return _IsBalance(_root);
//}
/*bool _IsBalance(Node* root)//时间复杂度为N*N
{
if (root == NULL)
{
return 0;
}
int left = _Depth(root->_left);
int right = _Depth(root->_right);
return abs(right - left) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}*/
//优化到O(N),把高度返回给上一层
bool _IsBalance(Node* root, int& depth)
{
if (root == NULL)//先判断是否为空
{
depth = 0;
return true;
}
int leftDepth = 0;
if (_IsBalance(root->_left, leftDepth) == false)//如果它不平衡直接return false
return false;
int rightDepth = 0;
if (_IsBalance(root->_right, rightDepth) == false)
return false;
if ((rightDepth - leftDepth) != root->_bf)
{
cout << "bf 异常" << root->_key << endl;
//return false;
}
depth = rightDepth > leftDepth ? rightDepth + 1 : leftDepth + 1;
return abs(rightDepth - leftDepth) < 2;
}
private:
Node* _root;
};
void test()
{
int a[] = { 16, 30, 7, 11, 9, 26, 18, 14, 19 };
AVLTree<int, int> tree;
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
tree.Insert(a[i], i);
}
tree.InOrder();
}