二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可以降低树的高度,从而减少平均搜索长度。
AVL树
一颗AVL树或者是空树,或者是具有以下性质的二叉搜索树:
- 它的左右子树都是AVL树
- 左右子树高度差(简称平衡因子)的绝对值不超过1(1、-1、0)
如果一颗二叉搜索树是高度平衡的,它就是AVL树。如果他有n个结点,其高度可保持在O(lgn),平均搜索的时间复杂度是O(lgn)
如果一棵树原来是平衡的二叉搜索树,现在向里面插入一个节点,造成了不平衡,这时候我们就要调整这棵树的结构,使之重新平衡。
规则:
- 如果插入一个结点,树的高度不变,则它就是平衡的
- 旋转之后树的高度不变,这里的不变是指与插入之前的高度相同
- 插入之后只会影响从插入点到根节点路径上的结点的平衡
- 平衡因子的取值只可能是-2,-1,0,1,2,取-2或2时就出现不平衡了,这时就需要调整,调整之后树就平衡
- 所有的插入都是建立在平衡二叉树的基础之上的
AVL树有三个值,一个_key(需要插入节点的值)、一个_value(某插入节点的下标)、一个_bf(平衡因子)。三个指针,左孩子指针,右孩子指针,父亲指针。
平衡二叉树结点的定义:
template<typename K, typename V>
struct AVLTreeNode
{
K _key;
V _value;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;
AVLTreeNode(const K& key, const V& value)
:_key(key)
, _value(value)
, _bf(0)
, left(NULL)
, _right(NULL)
, _parent(NULL)
{}
};
AVL树平衡调整
- 找平衡因子等于2 / -2
- 找插入新结点后失去平衡的最小子树
1) 距离插入结点最近
2) 平衡因子绝对值大于1的结点作为根 - 平衡调整
(1)左单旋【RR形 中为支,高左转】
当在子树c上插入一个新节点.此时子树c的高度变化为h+1,子树b的高度为h.结点5的平衡因子变为1,结点3的平衡因子变为2.此树不再平衡
左单旋:整体向左方向旋转。【RR形 中为支,高左转】
- 结点5的左子树b变为结点3的右子树
- 结点3以及其子树变为结点5的左子树
//左单旋 RR形
void RotateL(Node* parent)
{
Node* subR = parent->_right; //subR为parent结点的左孩子
Node* subRL = subR->_left; //subRL为subR的左孩子
parent->_right = subRL; //将subRL变为parent的右子树
//如果subRL非空 则设置它的父亲指针 即祖先结点
if (subRL)
{
subRL->_parent = parent;
}
Node* ppNode = parent->_parent; //记录下parent的父亲结点 即祖先结点
//将parent变为subR的左子树 并设置父亲指针
subR->_left = parent;
parent->_parent = subR;
//设置ppNode 若不为空 则将其指向subR
if (ppNode)
{
//判断parent是ppNode的左孩子还是右孩子 以方便设置subR的父亲结点
if (ppNode->_left == parent)
ppNode->_left = subR;
else
ppNode->_right = subR;
subR->_parent = ppNode;
}
else //若为空 则代表parent原来为根结点 则旋转后subR变为根节点
{
subR->_parent = NULL;
_root = subR;
}
//设置平衡因子 置零
subR->_bf = 0;
parent->_bf = 0;
}
(2)右单旋【LL形 中为支,高右转】
当在子树a的下面插入一个结点时,结点3的左子树高度变为h+1,右子树高度为h,平衡因子变为-1.结点5的左子树高度变为h+2,右子树高度变为h,平衡因子变为-2.此时不再平衡。
右单旋:整体向右方向旋转。【LL形 中为支,高右转】
- 结点5变为结点3的右子树
- 结点3以及其子树变为结点5的左子树
- parent指向结点3
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode)
{
if (ppNode->_left == parent)
ppNode->_left = subL;
else
ppNode->_right = subL;
subL->_parent = ppNode;
}
else
{
subL->_parent = NULL;
_root = subL;
}
subL->_bf = 0;
parent->_bf = 0;
}
(3)左右双旋【下二整体先左转 后右单旋】
新结点插入到子树b和c会有不同的情况,首先以在子树b下插入一个结点为例,此时结点4的平衡因子变为-1,结点3的平衡因子变为1,结点5的平衡因子变为-2. AVL树不再平衡。
左右双旋:先左单旋,再右单旋。不过两次的对象不同。先对结点3和结点4进行左单旋,然后进行右单旋。
新结点插入到子树b和c会有不同的情况:
(1)插入到b树下:
也就是我刚刚举例子的图:
(2)插入到c树下:
(3)a、b、c、d子树都为NULL,插入的结点本就是subLR
左右双旋结点插入小结:
- 结点subLR的左子树b都变成了结点subL的右子树
- 结点subRL的右子树c都变成了结点parent的左子树
- 平衡因子的计算公式为,_bf = 右子树高度-左子树高度
平衡因子的变化情况:
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = parent->_right;
int bf = subLR->_bf; //记录下旋转前subLR的平衡因子
//左右双旋
RotateL(parent->_left);
RotateR(parent);
3种情况
if (bf == 0) //情况三 a b c d都为NULL 且插入的结点就是subLR
{
parent->_bf = 0;
subL->_bf = 0;
subLR->bf = 0;
}
else if (bf == -1) //情况一 b子树下插入新结点
{
parent->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else //情况二 c子树下插入新结点
{
parent->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
}
(4)右左双旋【下二整体先右转 后左单旋】
同样也是有三种情况,这里我以新节点插入到c子树为例分析:
调整过程:
这里我直接给出三种情况平衡因子的总结:
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf; //记录旋转前subRL的平衡因子
RotateR(subR);
RotateL(parent);
if (bf == 0) //情况三
{
subR->_bf = 0;
parent->_bf = 0;
}
else if(bf == 1) //情况一
{
subR->_bf = 0;
parent->_bf = -1;
}
else
{
subR->_bf = 1;
parent->_bf = 0;
}
subRL->_bf = 0;
}
完整的平衡二叉树类
template<typename K, typename V>
struct AVLTreeNode
{
K _key;
V _value;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;
AVLTreeNode(const K& key, const V& value)
:_key(key)
, _value(value)
, _bf(0)
, _left(NULL)
, _right(NULL)
, _parent(NULL)
{}
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
//typedef AVLTreeNode<K, V>* PNode;
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;
}
bool IsAVLTree()
{
int Height = 0;
return _IsAVLTree(_root, Height);
}
void InOrder()
{
_InOrder(_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;
}
//左单旋 RR形
void RotateL(Node* parent)
{
Node* subR = parent->_right; //subR为parent结点的左孩子
Node* subRL = subR->_left; //subRL为subR的左孩子
parent->_right = subRL; //将subRL变为parent的右子树
//如果subRL非空 则设置它的父亲指针 即祖先结点
if (subRL)
{
subRL->_parent = parent;
}
Node* ppNode = parent->_parent; //记录下parent的父亲结点 即祖先结点
//将parent变为subR的左子树 并设置父亲指针
subR->_left = parent;
parent->_parent = subR;
//设置ppNode 若不为空 则将其指向subR
if (ppNode)
{
//判断parent是ppNode的左孩子还是右孩子 以方便设置subR的父亲结点
if (ppNode->_left == parent)
ppNode->_left = subR;
else
ppNode->_right = subR;
subR->_parent = ppNode;
}
else //若为空 则代表parent原来为根结点 则旋转后subR变为根节点
{
subR->_parent = NULL;
_root = subR;
}
//设置平衡因子 置零
subR->_bf = 0;
parent->_bf = 0;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode)
{
if (ppNode->_left == parent)
ppNode->_left = subL;
else
ppNode->_right = subL;
subL->_parent = ppNode;
}
else
{
subL->_parent = NULL;
_root = subL;
}
subL->_bf = 0;
parent->_bf = 0;
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = parent->_right;
int bf = 0;
if (subLR == NULL)
bf = 0;
else
bf = subLR->_bf; //记录下旋转前subLR的平衡因子
//左右双旋
RotateL(parent->_left);
RotateR(parent);
//3种情况
if (bf == 0) //情况三 a b c d都为NULL 且插入的结点就是subLR
{
parent->_bf = 0;
subL->_bf = 0;
}
else if (bf == -1) //情况一 b子树下插入新结点
{
parent->_bf = 1;
subL->_bf = 0;
}
else //情况二 c子树下插入新结点
{
parent->_bf = 0;
subL->_bf = -1;
}
//subLR->_bf = 0;
}
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf; //记录旋转前subRL的平衡因子
RotateR(subR);
RotateL(parent);
if (bf == 0) //情况三
{
subR->_bf = 0;
parent->_bf = 0;
}
else if(bf == 1) //情况一
{
subR->_bf = 0;
parent->_bf = -1;
}
else
{
subR->_bf = 1;
parent->_bf = 0;
}
subRL->_bf = 0;
}
void _InOrder(Node* root)
{
if (root == NULL)
return;
_InOrder(root->_left);
cout << root->_key << " " << root->_bf << endl;
_InOrder(root->_right);
}
int _Hight(Node* root)
{
if (root == NULL)
{
return 0;
}
int left = _Hight(root->left) + 1;
int right = _Hight(root->right) + 1;
return left > right ? left : right;
}
bool _IsAVLTree(Node *root, int &Height)
{
if (root == NULL)
{
Height = 0;
return true;
}
int leftHeight = 0;
if (_IsAVLTree(root->_left, leftHeight) == false)
{
return false;
}
int rightHeight = 0;
if (_IsAVLTree(root->_right, rightHeight) == false)
{
return false;
}
Height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
return abs(leftHeight - rightHeight) < 2;
}
private:
Node* _root;
};