1. AVL 树
1.1 AVL树的概念
二叉搜索树虽可以缩短查找的效率,但
如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当
于在顺序表中搜索元素,效率低下
。因此,两位俄罗斯的数学家
G.M.Adelson-Velskii
和
E.M.Landis
在
1962
年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之
差的绝对值不超过
1(
需要对树中的结点进行调整
)
,即可降低树的高度,从而减少平均搜索长度。
一棵
AVL
树或者是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是
AVL
树
左右子树高度之差
(
简称平衡因子
)
的绝对值不超过
1(-1/0/1)
![](https://img-blog.csdnimg.cn/84351ee039d8448c86ad0bbf8af5d12a.png)
如果一棵二叉搜索树是高度平衡的,它就是
AVL
树。如果它有
n
个结点,其高度可保持在
,搜索时
间复杂度
O( long2 n
)
。
1.2 AVL树节点的定义
template<class K,class V>
struct AVLtreeNode
{
AVLtreeNode(const pair<K,V> &kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{
}
AVLtreeNode<K, V>* _left;
AVLtreeNode<K, V>* _right;
AVLtreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf;
};
1.3 AVL树的插入
AVL
树就是在二叉搜索树的基础上引入了平衡因子,因此
AVL
树也可以看成是二叉搜索树。那么
AVL
树的插入过程可以分为两步:
1.
按照二叉搜索树的方式插入新节点
2.
调整节点的平衡因子
例如我们现在需要插入10 这个节点
这个时候我们需要旋转处理一下例如:
此时还是保持着平衡二叉树的性质
更新平衡因子有一下5中情况
1. cur == parent->_left parent->_bf--
2. cur == parent-> _right parent->bf++
3.更新以后,如果parent->_bf == 0, 说明更新结束,说明更新前parent->_bf 不是-1 就是 1,现在parent->_bf == 0,说明是把矮的那边给填上了。
4.更新以后,如果parent->_bf == 1 || parent->_bf == -1,说明更新前parent->_bf是0,现在变成1或者-1,说明插入节点是我的子树变高了,所以我的平衡因子也要改变
5.更新以后如果parent->_bf == 2 || parent->_bf == -2,说明已经不平衡了,需要旋转处理。
下面我先看右单旋的情况,具象图如下:
具体代码关系图如下:
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* parentparent = parent->_parent;
parent->_left = subLR;
if (subLR) //如果不为空才能更新parent
subLR->_parent = parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root) //如何parent == _root,说明旋转后根节点发生变化
{
subL = _root;
subL->_parent = nullptr;
}
else
{
if (parentparent->_left == parant)
parentparent->_left = subL;
else
parentparent->_right = subL;
}
subL->_bf = parent->_bf = 0; //更新平衡因子
}
左单旋的情况,具象图如下:
具体代码关系图如下:![](https://img-blog.csdnimg.cn/edf161f47ee54f2e894c6d1639b5b2bf.png)
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* parentparent = parent->_parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
parent->_parent = ssubR;
if (parentparent == nullptr)
{
subR = _root;
subR->_parent = nullptr;
}
else
{
if (parentparent->_left = parent)
parentparent->_left = subR;
else
parent->_right = subR;
}
subR->_bf = parent->_bf = 0;
}
下面我们看一下双旋的情况:
当parent->_bf = -2,cur->bf == 1时,要进行左右双旋(左单旋 + 右单旋),但是旋转结束的时候跟新平衡因子又有3中情况如下图:
代码如下:
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 0) //说明它本身就是新增节点
{
parent->_bf = 0;
subL->_bf = 0;
subLR->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else
{
assert(false);
}
}
当parent->_bf == 2 && cur->_bf == -1 时,需要进行右左双旋(右单旋+左单旋),但是同样平衡因子的跟新右三种情况:
代码如下:
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = -1;
subR->_bf = 0;
subRL->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 0;
subR->_bf = -1;
subRL->_bf = 0;
}
else
{
assert(false);
}
}
总结:
假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑
1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
当pSubR的平衡因子为1时,执行左单旋
当pSubR的平衡因子为-1时,执行右左双旋
2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
当pSubL的平衡因子为-1是,执行右单旋
当pSubL的平衡因子为1时,执行左右双旋
旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。
AVL树的验证
AVL
树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证
AVL
树,可以分两步:
1.
验证其为二叉搜索树
如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
2.
验证其为平衡树
每个节点子树高度差的绝对值不超过
1(
注意节点中如果没有平衡因子
)
节点的平衡因子是否计算正确
//验证AVL树
int Hegiht(Node* _root)
{
if (_root == nullptr)
return 0;
int left = Hegiht(_root->_left);
int right = Hegiht(_root->_right);
return left > right ? left + 1 : right + 1;
}
bool _IsBalanceTree(Node* _root)
{
if (_root == nullptr)
return true;
int lefttree = Hegiht(_root->_left);
int righttree = Hegiht(_root->_right);
int diff = abs(lefttree - righttree);
if (diff > 2)
{
return false;
}
return _IsBalanceTree(_root->_left) && _IsBalanceTree(_root->_right);
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
3. 验证用例
请同学们结合上述代码按照以下的数据次序,自己动手画
AVL
树的创建过程,验证代码是否有漏
洞。
常规场景
1
{16, 3, 7, 11, 9, 26, 18, 14, 15}
特殊场景
2
{4, 2, 6, 1, 3, 5, 15, 7, 16, 14}
![](https://img-blog.csdnimg.cn/0f285376fb254f46a15faa04716317a3.png)
AVL树的删除(了解)
因为
AVL
树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不错与删除不 同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。
具体实现可参考《算法导论》或《数据结构
-
用面向对象方法与
C++
描述》殷人昆版,删除比较麻烦,就算面试官让你实现,估计他也不知对错
AVL树的性能
AVL
树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过
1
,这样可以保证 查询时高效的时间复杂度,即 log2N。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。 因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变
)
,可以考虑
AVL
树, 但一个结构经常修改,就不太适合。
全部代码如下:
AVLTree.h
#pragma once
#include<iostream>
#include<assert.h>
template<class K, class V>
class AVLTreeNode
{
public:
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
std::pair<K, V> _kv;
int _bf; //平衡因子
AVLTreeNode(const std::pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _bf(0)
{
}
};
template<class K, class V>
class AVLTree
{
public:
typedef AVLTreeNode<K, V> Node;
AVLTree() :_root(nullptr)
{
}
bool insert(const std::pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur != nullptr)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
//更新平衡因子
while (parent != nullptr)
{
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 = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2) //旋转
{
if (parent->_bf == 2 && cur->_bf == 1) //左旋
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1) //右旋
{
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1) //右左双旋== 右单旋 + 左单旋
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1) //左右双旋 == 左单旋 + 右单旋
{
RotateLR(parent);
}
else
{
assert(false);
}
break;
}
else
{
assert(false);
}
}
return true;
}
public:
//验证AVL树
int Hegiht(Node* _root)
{
if (_root == nullptr)
return 0;
int left = Hegiht(_root->_left);
int right = Hegiht(_root->_right);
return left > right ? left + 1 : right + 1;
}
bool _IsBalanceTree(Node* _root)
{
if (_root == nullptr)
return true;
int lefttree = Hegiht(_root->_left);
int righttree = Hegiht(_root->_right);
int diff = abs(lefttree - righttree);
if (diff > 2)
{
return false;
}
return _IsBalanceTree(_root->_left) && _IsBalanceTree(_root->_right);
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
private:
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* parentparent = parent->_parent;
parent->_right = subRL;
if (subRL != nullptr)
subRL->_parent = parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (parentparent->_left == parent)
parentparent->_left = subR;
else
parentparent->_right = subR;
subR->_parent = parentparent;
}
subR->_bf = parent->_bf = 0;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* parentpanret = parent->_parent;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (parentpanret->_left == parent)
parentpanret->_left = subL;
else
parentpanret->_right = subL;
subL->_parent = parentpanret;
}
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 == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = -1;
subR->_bf = 0;
subRL->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 0;
subR->_bf = -1;
subRL->_bf = 0;
}
else
{
assert(false);
}
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 0) //说明它本身就是新增节点
{
parent->_bf = 0;
subL->_bf = 0;
subLR->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else
{
assert(false);
}
}
public:
void inoder()
{
Node* cur = _root;
Node* MostRight = nullptr;
while (cur != nullptr)
{
MostRight = cur->_left;
if (MostRight != nullptr)
{
while (MostRight->_right != nullptr && MostRight->_right != cur)
{
MostRight = MostRight->_right;
}
if (MostRight->_right == nullptr)
{
MostRight->_right = cur;
cur = cur->_left;
continue;
}
else
{
MostRight->_right = nullptr;
}
}
std::cout << cur->_kv.first << ":" << cur->_kv.second << std::endl;
cur = cur->_right;
}
}
private:
Node* _root;
};
AVLTree.cpp
#include"AVLTree.h"
void AVLTreeTest()
{
AVLTree<int, int> av;
//int array[] = { 9,5,4,3,2,6,8,1 };
//int array[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
int array[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16,14 };
for (const auto& e : array)
{
av.insert(std::pair<int,int>(e,e));
//av.inoder();
std::cout << av.IsBalanceTree() << std::endl;
}
av.inoder();
}
int main()
{
AVLTreeTest();
}