目录
嗨!收到一张超美的风景图,愿你每天都能顺心!
前言
搜索二叉树请查看本篇博文:【C++】搜索二叉树底层实现_花果山~程序猿的博客-CSDN博客
一,AVL概念
![](https://i-blog.csdnimg.cn/blog_migrate/74e084d56f9fbf51e6f7fc524407c003.png)
二,基础框架
为方便循序渐进的学习,这里只放最出初始的树结点定义。
template <class K, class V>
class AVL_Data
{
public:
pair<K, V> _kv;
AVL_Data<K, V>* left = nullptr;
AVL_Data<K, V>* right = nullptr;
AVL_Data<K, V>* parent = nullptr;
int _bf = 0; // ballance factor
AVL_Data(const pair<K, V>& p)
:_kv(p)
{}
};
上面定义在后面会进行完善修改。
三,insert
根据前面搜索二叉树的经验我们能快速写完插入函数,但AVL树是特殊的搜索二叉树,我们需要对树的高度进行调整。那么我们插入时就会遇到三种情况:
1. 插入三种情况
情况一:
情况二:
情况三:
代码实现如下:
template <class K, class V>
class AVL_Tree
{
typedef AVL_Data<K, V> AVL_Data;
AVL_Data* root = nullptr;
public:
bool insert(const pair<K, V>& p)
{
AVL_Data* new_a_d = new AVL_Data(p);
if (!root)
{
root = new_a_d;
}
else
{
AVL_Data* cur = root;
AVL_Data* parent = nullptr;
while (cur)
{
if (p.first < cur->_kv.first)
{
parent = cur;
cur = cur->left;
}
else if (p.first > cur->_kv.first)
{
parent = cur;
cur = cur->right;
}
else
{
delete(new_a_d); // 插入失败,删除新建结点
return false;
}
}
if (p.first < parent->_kv.first)
{
parent->left = new_a_d;
}
else
{
parent->right = new_a_d;
}
new_a_d->parent = parent;
cur = new_a_d;
//完成插入,进行平衡
while (parent)
{ // 插入,修改parent平衡因子
if (cur == parent->right)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
// 判断parent平衡因子是否是0,如果非0则需要向祖先更新平衡因子
if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->parent;
}
else if (parent->_bf == 0)
{
break;
}
else if(parent->_bf == 2 || parent->_bf == -2)
{
// 处理绝对值大于1,下面代码目的是记录未修改的平衡因子。
// 需要旋转处理,这个我们下面再讲
cur = parent;
parent = parent->parent;
}
else
{
// 出现其他情况,在插入时这棵AVL树本身就是异常AVL树
assert(false);
}
}
return true;
}
}
};
2. 四种旋转方法
法一:左单旋法
我们以下面图为讲解例子,a,b,c表示的是子树。
h 表示子树的高度。
请看下面场景:
h = 3, 4...组合方式会更多,这里画出图没什么意义,问题是失去平衡我们如何解决??
通过下面方法解决:
总结:
1. 右边高,则向左旋转。
2. C树发生插入,平衡因子发生改变,进而发生旋转。
void RotateL(AVL_Data* parent)
{
assert(parent->right);
AVL_Data* par = parent;
AVL_Data* par_R = par->right;
AVL_Data* par_RL = par->right->left;
AVL_Data* ppnode = par->parent;
par->right = par_RL;
if (par_RL)
par_RL->parent = par;
par_R->left = par;
par->parent = par_R;
par_R->parent = ppnode;
if (!ppnode)
{
root = par_R;
}
else if (ppnode->left == par)
{
ppnode->left = par_R;
}
else
{
ppnode->right = par_R;
}
par->_bf = 0;
par_R->_bf = 0;
}
// 实验例子
AVL_Tree<int, string> tree;
tree.insert(make_pair(30 , "李四"));
tree.insert(make_pair(20, "二麻子"));
tree.insert(make_pair(60, "张三"));
tree.insert(make_pair(45, "王五"));
tree.insert(make_pair(75, "王五"));
tree.insert(make_pair(65, "王五"));
法二:右单旋法
思路跟左旋法差不多,图像是相反,这里就只给场景解法模板:
h = 0, 1, 2的发生场景:
学会了法一自然会了法二:
void RotateR(AVL_Data* parent)
{
assert(parent->left);
AVL_Data* par = parent;
AVL_Data* par_L = par->left;
AVL_Data* par_LR = par->left->right;
AVL_Data* ppnode = par->parent;
par->left = par_LR;
if (par_LR)
par_LR->parent = par;
par_L->right = par;
par->parent = par_L;
par_L->parent = ppnode;
if (!ppnode)
{
root = par_L;
}
else if (ppnode->left == par)
{
ppnode->left = par_L;
}
else
{
ppnode->right = par_L;
}
par->_bf = 0;
par_L->_bf = 0;
}
法三:先左后右双旋法
跟单旋一样,我们首先展示,当h = 0,1,2时需要左右双旋处理的场景。
双旋法步骤变化流程,如下:
从结果来看,就是将60这个位置推上去置于“根”。
代码如下:
void RotateLR(AVL_Data* parent)
{
assert(parent->left);
AVL_Data* par = parent;
AVL_Data* par_L = par->left;
AVL_Data* par_LR = par->left->right;
AVL_Data* ppnode = par->parent;
int par_LR_bf = par_LR->_bf;
RotateL(par_L);
RotateR(par);
if (par_LR_bf == -1)
{
par->_bf = 1;
par_L->_bf = 0;
}
else if (par_LR_bf == 1)
{
par->_bf = 0;
par_L->_bf = -1;
}
else if (par_LR_bf == 0)
{
par->_bf = 0;
par_L->_bf = 0;
}
else
{
assert(false);
}
par_LR->_bf = 0;
}
// 测试案例
void Test_insert_L()
{
AVL_Tree<int, string> tree;
tree.insert(make_pair(90, "李四"));
tree.insert(make_pair(30, "二麻子"));
tree.insert(make_pair(100, "张三"));
tree.insert(make_pair(25, "王五"));
tree.insert(make_pair(60, "王五"));
tree.insert(make_pair(50, "王五"));
}
法四:先右后左双旋法
我们学会法三后,照葫芦画瓢即可。
各场景:
代码:
void RotateRL(AVL_Data* parent)
{
assert(parent->right);
AVL_Data* par = parent;
AVL_Data* par_R = par->right;
AVL_Data* par_RL = par->right->left;
AVL_Data* ppnode = par->parent;
int par_RL_bf = par_RL->_bf;
RotateR(par_R);
RotateL(par);
if (par_RL_bf == -1)
{
par->_bf = 0;
par_R->_bf = 1;
}
else if (par_RL_bf == 1)
{
par->_bf = -1;
par_R->_bf = 0;
}
else if (par_RL_bf == 0)
{
par->_bf = 0;
par_R->_bf = 0;
}
else
{
assert(false);
}
par_RL->_bf = 0;
}
// 测试案例
void Test_insert_L()
{
AVL_Tree<int, string> tree;
tree.insert(make_pair(30, "李四"));
tree.insert(make_pair(20, "二麻子"));
tree.insert(make_pair(90, "张三"));
tree.insert(make_pair(15, "王五"));
tree.insert(make_pair(60, "王五"));
tree.insert(make_pair(100, "王五"));
tree.insert(make_pair(55, "王五"));
tree.insert(make_pair(67, "王五"));
tree.insert(make_pair(95, "王五"));
tree.insert(make_pair(50, "王五"));
}
测试(判断一棵树是否是AVL树)
思路:
1. 检查高度(AVL中每棵子树都是AVL树)。
2. 检查平衡因子是否正确。
代码如下:
int Hight(const AVL_Data* root)
{
if (root == nullptr)
return 0;
int left_H = Hight(root->left);
int left_R = Hight(root->right);
return left_H >= left_R ? left_H + 1 : left_R + 1;
}
bool B_balance()
{
return _B_balance(root);
}
bool _B_balance(const AVL_Data* root)
{
if (root == nullptr)
return true;
int left_root = Hight(root->left);
int right_root = Hight(root->right);
if ((right_root - left_root) != root->_bf) // 利用Hight,进行平衡因子判断
return false;
return abs(left_root - right_root) < 2 && _B_balance(root->left) && _B_balance(root->right);
}
3. 随机值案例
用这个代码多跑几次,差不多能遍历所有环境。
void Random_Test()
{
srand(time(0));
const size_t N = 10000000;
AVL_Tree<int, int> t;
for (size_t i = 0; i < N; i++)
{
size_t x = rand();
t.insert(make_pair(x, x));
}
cout << t.B_balance() << endl;
}
快来测试自己的代码吧
insert全代码
bool insert(const pair<K, V>& p)
{
AVL_Data* new_a_d = new AVL_Data(p);
if (!root)
{
root = new_a_d;
}
else
{
AVL_Data* cur = root;
AVL_Data* parent = nullptr;
while (cur)
{
if (p.first < cur->_kv.first)
{
parent = cur;
cur = cur->left;
}
else if (p.first > cur->_kv.first)
{
parent = cur;
cur = cur->right;
}
else
{
delete(new_a_d); // 插入失败,删除新建结点
return false;
}
}
if (p.first < parent->_kv.first)
{
parent->left = new_a_d;
}
else
{
parent->right = new_a_d;
}
new_a_d->parent = parent;
cur = new_a_d;
//完成插入,进行平衡
while (parent)
{ // 插入,修改parent平衡因子
if (cur == parent->right)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
// 判断parent平衡因子是否是0,如果非0则需要向祖先更新平衡因子
if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->parent;
}
else if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == -2 || parent->_bf == 2)
{
if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
// cout << "RotateL" << endl;
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
// cout << "RotateR" << endl;
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
// cout << "RotateLR" << endl;
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
// cout << "RotateRL" << endl;
}
else
{
// 出现其他情况,在插入时这棵AVL树本身就是异常AVL树
// 问题出现在旋转方法
assert(false);
}
break;
}
else
{
assert(false);
}
}
return true;
}
}
四,删除
下期预告: 红黑树!!!
结语
本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力。