AVL树又称作高度平衡二叉树,它实际上是一种优化了的搜索二叉树。我们知道,由于二叉搜索树存在缺陷,有可能会退化成单链表,这样的话搜索的效率就降低了。为了将二叉搜索树的效率控制在O(logN)的级别,所以我们要给二叉搜索树加上一些条件,使得二叉搜索树高度平衡,时间复杂度为O(logN)。
概念:
1、首先AVL树是一颗二叉搜索树。
2、其次它的左子树和右子树都是AVL树。
3、它的左子树和右子树的高度差不超过1。(通常情况下,为每个结点都加一个平衡因子(bf),这个平衡因子是右子树的高度减去左子树的高度)
例:
二、平衡化旋转
如果一棵树原来是平衡的二叉搜索树,现在向里面插入一个结点,造成了不平衡,这时候我们就要调整这棵树的结构,使之重新平衡。首先我们来看看造成不平衡的结构都有哪几种以及如何调整。
首先关于平衡我有以下几点需要强调一下:
1、如果插入一个结点,树的高度不变,则它就是平衡的。
2、旋转之后树的高度不变,这里的不变是指与插入之前的高度相同。
3、插入之后只会影响从插入点到根节点路径上的结点的平衡。
4、平衡因子的取值只可能是-2,-1,0,1,2,取-2或2时就出现不平衡了,这时就需要调整,调整之后树就平衡了。
5、注意,所有的插入都是建立在平衡二叉树的基础之上的。
关于左右双旋,他们的平衡因子有以下几种情况:
右左双旋与左右双旋是对称的,可参考左右双旋的出结论。
2、不仅是插入会破话树的平衡,删除也会破坏树的平衡,下面我们来分析一下删除的情况。首先必须明确删除只会影响从删除点到根节点路径上的结点,不会影响删除点之后的结点。
当然删除与二叉搜索树的删除一样,将删除结点有两个孩子的这种情况转换成只有一个孩子或者没有孩子的情况,然后再进行删除。当然删除之后树还要保持平衡。如果只有一个根节点的话直接删除就行。
代码:
#pragma once
#include<queue>
using namespace std;
template<typename K,typename V>
struct AVLTreeNode
{
K _key;
V _value;
int _bf; //平衡因子,右子树的高度减去左子树的高度
AVLTreeNode<K, V> *_left;
AVLTreeNode<K, V> *_right;
AVLTreeNode<K, V> *_parent;
AVLTreeNode(K key, V value)
:_key(key)
, _value(value)
, _bf(0)
, _left(NULL)
, _right(NULL)
, _parent(NULL)
{}
};
template<typename K,typename V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree()
:_root(NULL)
{}
AVLTree(const AVLTree<K,V>& tree)
:_root(NULL)
{
_Copy(tree._root,_root);
}
AVLTree<K, V>& operator=(const AVLTree<K,V>& avl)
{
if (this != &avl)
{
AVLTree<K, V> tmp(avl);
swap(_root,tmp._root);
}
return *this;
}
~AVLTree()
{
_Destory(_root);
}
bool Insert(const K& key,const V& value)
{
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!=NULL) //树不是空树
{
if (parent->_key < key)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
}
else //树是空树
{
_root = cur;
return true;
}
if (cur == parent->_left) //如果插入的位置是父节点的左孩子
parent->_bf--;
else
parent->_bf++; //如果插入的位置是父节点的右孩子
while (parent != NULL) //沿着插入位置到根节点回溯,查找平衡因子不满足的结点
{
if (parent->_bf == 0) //表明插入之后parent这棵树的高度没变,因此这时就是AVL树
{
return true;
}
else if (parent->_bf == 1 || parent->_bf == -1) //parent这棵树满足AVL,因为parent的高度发生变化,还要向上查找祖先结点
{
Node* ppNode = parent->_parent; //保存parent的父节点
if (ppNode != NULL)
{
if (ppNode->_left == parent) //ppNode的左子树高度发生变化
ppNode->_bf--;
else //ppNode的右子树高度发生变化
ppNode->_bf++;
}
parent = parent->_parent; //当前树满足AVL树,所以继续向上判断
}
else //如果当前树不满足AVL树,则就进行旋转恢复平衡
{
if (parent->_bf==2) //如果parent的右子树高
{
if (parent->_right->_bf == 1) //parent一定不是叶子节点,所以他的孩子不为空
RotateL(parent); //因为子树与parent一样都是右子树高,进行单左旋
else //parent的右子树高,它的孩子左子树高,右左双旋
RotateRL(parent);
}
else if(parent->_bf==-2) //parent的左子树高度高
{
if (parent->_left->_bf == -1) //parent孩子的左子树高
RotateR(parent); //右单旋
else //parent的左子树高,它的孩子右子树高,左右双旋
RotateLR(parent);
}
break; //旋转之后这棵树的高度恢复成原来的高度
}
}
return true;
}
bool Remove(const K& key)
{
Node* parent = NULL;
Node* cur = _root;
Node* del = NULL; //用来指向要删除的位置
while (cur) //寻找要删除的位置
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key>key)
{
cur = cur->_left;
}
else
break;
}
if (cur == NULL) //删除失败
return false;
del = cur;
//如果要删除的结点有两个孩子,则寻找右子树最左结点,将问题转换成只有一个孩子或没有孩子的形式
if (cur->_left != NULL&&cur->_right != NULL)
{
cur = cur->_right; //cur的右孩子一定不为空
while (cur->_left)
{
cur = cur->_left;
}
del->_key = cur->_key;
del->_value = cur->_value;
del = cur; //交换之后让del指向这个要删除的结点
}
parent = cur->_parent; //让parent指向要删除的结点的父亲
if (cur->_left == NULL) //要删除的结点的左孩子为空,或者都为空
{
if (parent == NULL)
{
_root = cur->_right;
if (cur->_right) //如果cur有右孩子,则右孩子直接做根
cur->_right->_parent = NULL;
}
else
{
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
if (cur->_right) //cur的右子树不为空
cur->_right->_parent = parent;
}
cur = del->_right; //cur更新到要删除结点的右子树
}
else //要删除的结点的右孩子为空,左孩子不为空
{
if (parent == NULL) //如果要删除的结点是头结点
_root = cur->_left;
else //删除的结点不是根节点,则进行链接
{
if (parent->_left == cur)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
cur->_left->_parent = parent;
}
cur = del->_left; //cur更新到要删除结点的左子树
}
//因为要删除的结点之后的子树的高度不变,不需要修改,需要从parent向上判断是否平衡
while (parent) //只要parent不为空,就有可能不平衡
{
//调整parent的平衡因子
if (parent->_left == cur) //删除的是parent的左子树
parent->_bf++;
else
parent->_bf--;
if (parent->_bf == 1 || parent->_bf == -1) //原来平衡,删除一个后树高度不变,整棵树已经平衡
{
break;
}
//平衡因子原来不为0,删除一个高的子树之后变为0,需要继续向上寻找
if (parent->_bf != 0) //平衡因子原来不为0,且比较矮的子树被删除
{
if (cur == NULL)
{
if (parent->_left == NULL)
cur = parent->_right;
else
cur = parent->_left;
}
else
{
if (parent->_left == cur) //原来parent的比较矮的左子树被删除,让cur指向较高的子树
cur = parent->_right;
else
cur = parent->_left;
}
if (cur->_bf == 0) //cur的平衡因子为0,单旋转就可以平衡,而且parent这棵树的高度不变
{
if (parent->_bf < 0) //parent的左子树高,进行右旋转
{
RotateR(parent); //parent旋转后指向这棵旋转子树的新根
parent->_bf=1;
parent->_right->_bf= -1;
}
else //进行左旋转
{
RotateL(parent);
parent->_bf =-1;
parent->_left->_bf =1;
}
break;
}
//如果parent与较高的子树同号,则进行单旋转,旋转自后树的高度没有恢复成删除之前的,所以继续向上找
int d = parent->_bf - cur->_bf; //用d来判断是否同号,同号的话为1或-1,因为parent->_bf为2或-2,cur->_bf为1或-1
if (d == 1 || d == -1)
{
if (d == 1) //右子树高,要进行左旋
RotateL(parent);
else
RotateR(parent); //左子树高,进行右旋
}
else //要双旋处理,因为异号,所以d为3或-3
{
if (d == 3) //parent->_bf为2,cur->_bf为-1,右左双旋
RotateRL(parent);
else
RotateLR(parent);
}
}
cur = parent;
parent = parent->_parent;
}
delete del;
return true;
}
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
cur = cur->_right;
else if (cur->_key>key)
cur = cur->_left;
else
return true;
}
return false;
}
void InOder()
{
_InOder(_root);
}
protected:
void _Copy(Node* root,Node *&newroot)
{
if (root == NULL)
return;
Node* node = new Node(root->_key, root->_value);
node->_bf = root->_bf;
newroot = node;
node->_parent = newroot;
_Copy(root->_left,newroot->_left);
_Copy(root->_right, newroot->_right);
}
void _Destory(Node* root)
{
if (root == NULL)
return;
_Destory(root->_left);
_Destory(root->_right);
delete root;
}
void _InOder(Node* root)
{
if (root == NULL)
return;
_InOder(root->_left);
cout << root->_key << " " << root->_bf << endl;
_InOder(root->_right);
}
void RotateL(Node *&parent) //左单旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* ppNode = parent->_parent; //保存parent的父节点
parent->_right = subRL;
if (subRL != NULL)
subRL->_parent = parent;
subR->_left = parent;
parent->_parent = subR;
if (ppNode == NULL)
{
_root = subR;
subR->_parent = NULL;
}
else
{
if (ppNode->_left == parent)
ppNode->_left = subR; //让旋转的这棵树的根节点重新与上层链接
else
ppNode->_right = subR; //让旋转的这棵树的根节点重新与上层链接
subR->_parent = ppNode;
}
parent->_bf = subR->_bf = 0; //subR和parent又归于平衡
parent = subR; //让parent重新指向这棵树的根
}
void RotateR(Node * &parent) //右单旋
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* ppNode = parent->_parent; //保存parent的父节点
parent->_left = subLR;
if (subLR != NULL)
subLR->_parent = parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode == NULL) //如果parent是空
{
_root = subL;
subL->_parent = NULL;
}
else
{
if (ppNode->_left == parent)
ppNode->_left = subL; //让旋转的这棵树的根节点重新与上层链接
else
ppNode->_right = subL; //让旋转的这棵树的根节点重新与上层链接
subL->_parent = ppNode;
}
parent->_bf = subL->_bf=0; //parent与subL又趋于平衡
parent = subL; //让parent重新指向这棵树的根
}
void RotateLR(Node *&parent) //左右双旋
{
Node* tmp = parent->_left ;
Node* tmp2 = parent;
Node* subL = parent->_left;
Node* subLR = subL->_right;
int d1 = 0;
int d2 = 0;
if (subLR->_bf==1) //右子树子树变高
d1 =-1;
if (subLR->_bf == -1) //左子树高
d2 = 1;
RotateL(tmp);
RotateR(tmp2);
subL->_bf = d1;
parent->_bf=d2;
parent = subLR; //让parent重新指向这棵树的根
}
void RotateRL(Node* &parent) //右左双旋
{
Node* tmp = parent->_right;
Node* tmp2 = parent;
Node* subR = parent->_right; //记录subR和parent的平衡因子
Node* subRL = subR->_left;
int d1 = 0;
int d2 = 0;
if (subRL->_bf == -1)
d1 = 1;
if (subRL->_bf == 1)
d2 = -1;
RotateR(tmp);
RotateL(tmp2);
subR->_bf = d1;
parent->_bf = d2;
parent = subRL; //让parent重新指向这棵树的根
}
private:
Node* _root;
};
判断一颗搜索二叉树是不是AVL树,要求将时间优化到O(N).
思路:从根节点向上开始判断,并将高度传回上一层。
bool IsBlance()
{
bool flag = false;
_IsBlance(_root,flag);
return flag;
}
int _IsBlance(Node* root, bool &flag)
{
if (root == NULL)
{
flag = true;
return 0;
}
int L=_IsBlance(root->_left,flag);
if (flag == false)
return 0;
int R=_IsBlance(root->_right,flag);
if (flag == false)
return 0;
if (abs(R - L) < 2) //满足平衡条件
{
return L>R ? L + 1 : R + 1; //返回高的子树高度+1
}
else
flag = false;
return 0;
}