目录
1.AVL树的结构
![](https://i-blog.csdnimg.cn/direct/874e34fc4ab748d2aacb04c7a9bec6be.png)
2.AVL树节点的创建
首先要去清楚一下每个节点需要一些什么东西:
1)最重要的肯定是存储的数据;
2)然后又因为是一颗树,肯定要包含左右指针指向左孩子和右孩子;
3)因为AVL树是平衡的,但是这种平衡是需要去维护的,因此还需要有平衡因子(我这里定义为右子树高度 - 左子树高度)以及父节点;
这里我实现的AVL并不是一个完全泛型,而是用的一个key_value模型:
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf;
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _bf(0)
{}
};
3.insert(插入)的实现(部分)
遵循二叉搜索树的结构去插入即可,即左孩子比根小,右孩子比根大
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first > kv.first)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
}
但是这只是搜索二叉树的插入操作,这时AVL树,我们需要去调整它的平衡因子
如果插入在左边,则该节点的bf--,若在右边,则++:
if (parent->_left == cur)
{
parent->_bf--;
}
else if (parent->_right == cur)
{
parent->_bf++;
}
若parent->_bf == 0时,就不用再次向上调整,因为它 ++或者--之前 原本平衡因子是1或-1,变成0后不会影响祖先的平衡因子
若parent->_bf == 1 或 -1时,则需要继续往上更新,因为它 ++或者--之前 原本平衡因子是0(为什么不能是2 ? -> 因为在插入数据之前AVL树肯定是平衡的、已经调整过的),插入到哪边都会影响到祖先的平衡因子
若为 2 或 -2,则需要进行旋转处理,因为已经打破了规则
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) // 1 -1 -> 2 -2
{
......
}
4.关于AVL树的旋转问题
1)右单旋
这时,就会变成 ->
60的平衡因子就变成了-2,破坏了AVL的规则,需要旋转,如何旋转呢:
将30变成根,60变为30的右,30的右变为60的左 -> 这就是右单旋
条件: parent->_bf == -2, cur->_bf == -1
2)左单旋
在c的左或者右插入一个数:
->
30的平衡因子就变成了2,破坏了AVL的规则,需要旋转,如何旋转呢:
将60变成根,30变为60的左,60的左变为30的右 ->这就是左单旋
条件: parent->_bf == 2, cur->_bf == 1
3)左右单旋
这时在b或者c的子孩子位置插入数据,则会导致
90的平衡因子变为了-2,30的平衡因子变为了1
这个时候,单纯的右单旋是解决不了问题的,因为旋转之后还是会出现2这个平衡因子
这时候就需要先对30进行左单旋(将其变为右单旋的情况后),再对90进行右单旋
即RotateL(30), 然后RotateR(90),但是这样之后并没有完全解决,因为两个旋转叠加,导致平衡因子错乱,需要我们自己调整
分几种情况去讨论旋转后的平衡因子调节:
第一种:
在b处插入数据,导致60的平衡因子变为-1的:
使得parent->_bf = 1、subL->_bf = 0、subLR->_bf = 0。
第二种:
在c处插入数据,导致60的平衡因子变为1的:
使得parent->_bf = 0、subL->_bf = -1、subLR->_bf = 0。
第三种:
60即为将要新增的位置:
使得parent->_bf = 0、subL->_bf = 0、subLR->_bf = 0。
4)右左单旋
右左单旋与左右单旋极其相似:(这里就直接给结论了)
5.find查找功能
直接遵循搜索二叉树的规则进行查找即可
Node* find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key < cur->_kv.first)
{
cur = cur->_left;
}
else if (key > cur->_kv.first)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
6. Is_balance判断是否平衡功能
递归实现,学习过二叉树的应该简单:
bool Is_balance()
{
return _Is_balance(_root);
}
bool _Is_balance(Node* root)
{
if (root == nullptr)
return true;
int LH = _Height(root->_left);
int RH = _Height(root->_right);
if (abs(LH - RH) >= 2)
return false;
if (RH - LH != root->_bf)
return false;
return _Is_balance(root->_left) && _Is_balance(root->_right);
}
7.测试
Inorder只需要中序遍历一遍即可,这样就可以得到升序
void TestTree1()
{
int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
AVLTree<int, int> a1;
for (int e : a)
{
if (e == 4)
{
int i = 0;
}
a1.Insert({ e, e });
cout << "插入值为" << e << "时,树是否平衡 - 》" << a1.Is_balance() << endl;
}
a1.Inorder();
}
8.完整代码
#pragma once
#include <iostream>
#include <vector>
#include <assert.h>
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf;
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _bf(0)
{}
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
void RotateR(Node* parent)
{
Node* ppNode = parent->_parent;
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
parent->_bf = subL->_bf = 0;
}
void RotateL(Node* parent)
{
Node* ppNode = parent->_parent;
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppNode->_right == parent)
{
ppNode->_right = subR;
}
else
{
ppNode->_left = subR;
}
subR->_parent = ppNode;
}
subR->_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)
{
subRL->_bf = 0;
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 1)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == 0)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_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 == -1)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == 0)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first > kv.first)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
// 更新平衡因子bf
while (parent)
{
if (parent->_left == cur)
{
parent->_bf--;
}
else if (parent->_right == cur)
{
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) // 1 -1 -> 2 -2
{
// 当前子树出问题了,需要旋转平衡一下
if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(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;
}
}
return true;
}
Node* find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key < cur->_kv.first)
{
cur = cur->_left;
}
else if (key > cur->_kv.first)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
void Inorder()
{
_Inorder(_root);
cout << endl;
}
int Height()
{
return _Height(_root);
}
bool Is_balance()
{
return _Is_balance(_root);
}
int Size()
{
return _Size(_root);
}
private:
int _Size(Node* root)
{
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
return max(_Height(root->_left), _Height(root->_right)) + 1;
}
bool _Is_balance(Node* root)
{
if (root == nullptr)
return true;
int LH = _Height(root->_left);
int RH = _Height(root->_right);
if (abs(LH - RH) >= 2)
return false;
if (RH - LH != root->_bf)
return false;
return _Is_balance(root->_left) && _Is_balance(root->_right);
}
void _Inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_Inorder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_Inorder(root->_right);
}
Node* _root = nullptr;
};
void TestTree1()
{
int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
AVLTree<int, int> a1;
for (int e : a)
{
if (e == 4)
{
int i = 0;
}
a1.Insert({ e, e });
cout << "插入值为" << e << "时,树是否平衡 - 》" << a1.Is_balance() << endl;
}
a1.Inorder();
}
void TestTree2()
{
const int N = 10000000;
vector<int> v;
v.reserve(N);
srand(time(0));
// 插入随机数
for (size_t i = 0; i < N; i++)
{
v.push_back(rand() + i);
}
// 测试AVLTree插入速度
size_t begin2 = clock();
AVLTree<int, int> t;
for (auto e : v)
{
t.Insert(make_pair(e, e));
}
size_t end2 = clock();
cout << "Insert:" << end2 - begin2 << endl;
cout << "Height:" << t.Height() << endl;
cout << "Size:" << t.Size() << endl;
// 测试ALVTree查找速度
size_t begin1 = clock();
for (auto e : v)
{
t.find(e);
}
size_t end1 = clock();
cout << "Find:" << end1 - begin1 << endl;
}