✨专栏:C/C++
目录
一、AVL树的概念
在二叉搜索树中,如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。为此两位科学家就发明了AVL树:当向二叉搜索树中插入新节点后,如果能保证每个节点的左右子树高度之差的绝对值不超过1(需要对树中的节点进行调整),即可降低树的高度,从而减少平均搜索长度。
一颗AVL树或者是空树,或者是具有以下性质的二叉搜索树:
• 它的左右子树都是AVL树
• 左右子树高度之差(平衡因子)的绝对值不超过1(-1/0/1)
如果一颗二叉搜索树是高度平衡的,它就是AVL树。如果它有n个节点,其高度可保持在O(log₂N),搜索时间复杂度O(log₂N)。
二、AVL树的实现
基本框架:
#pragma once
#include<iostream>
using namespace std;
template<class K,class V>
struct AVLTreeNode
{
pair<K, V> _kv;//节点里面存的值
AVLTreeNode<K, V>* _left;//该节点的左孩子
AVLTreeNode<K, V>* _right;//该节点的右孩子
AVLTreeNode<K, V>* _parent;//该节点的双亲
int _bf;//该节点的平衡因子
AVLTreeNode(const pair<K,V>& kv)
:_kv()
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
{}
};
template<class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;//给节点取别名
public:
//......
private:
Node* _root = nullptr;
};
• pair<K, V> _kv; 节点里面存的值(键值对)
• AVLTreeNode<K, V>* _left; 该节点的左孩子
• AVLTreeNode<K, V>* _right; 该节点的右孩子
• AVLTreeNode<K, V>* _parent; 该节点的双亲
• int _bf; 该节点的平衡因子
🌟插入
若树为空,则直接新增节点,赋值给root指针:
bool Insert(const pair<K,V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
//树不为空
}
树不为空,按二叉搜索树的性质查找插入位置,插入新节点:
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 (cur->_kv.first < kv.first)//cur < kv 往右走
{
parent = cur;//cur更新parent也要更新
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)// cur > kv 往左走
{
parent = cur;
cur = cur->_left;
}
else
{
//找到相同的数,返回false
return false;
}
}
//插入新节点
cur = new Node(kv);
if (parent->_kv.first < kv.first)//判断新插入的节点是在parent的左边还是右边
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;//与parent链接
//更新平衡因子...
return true;
}
这里的平衡因子,并不是实现AVL树的,唯一方式,有一些会直接去看高度差来进行控制,但是我们这里采用的是平衡因子。
• 平衡因子=右子树的高度 - 左子树的高度;(反之也可以,可按自己喜欢的方式去实现)
插入节点,会影响部分祖先节点的平衡因子,因此我们需要更新平衡因子,
插入在左子树,平衡因子--;
插入在右子树,平衡因子++;
是否往上更新祖先的平衡因子,要看parent所在子树的高度是否变化:
1、parent的平衡因子 == 0
• 说明parent的平衡因子更新前是1 or -1 ,插入节点插入在矮的那边,
• parent所在子树的高度不变,不需要继续往上更新;
2、parent的平衡因子 == 1/-1
• 说明parent的平衡因子更新前是0,插入节点在任意一边;
• parent所在的子树高度都会变化,需要继续往上更新;
3、parent的平衡因子 == 2/-2
• 说明parent的平衡因子更新前是 1/-1 ,插入节点插入在高的那边;
• 进一步加剧了parent所在的子树的不平衡,已经违反规则(AVL树的严格平衡),
需要旋转处理。
//更新平衡因子
while (parent)
{
//更新双亲的平衡因子
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)
{
RotateLR(parent);//左右双旋
}
else
{
RotateRL(parent);//右左双旋
}
break;
}
else
{
assert(false);//出现其他情况,即出现错误,进行断言
}
}
上面所说的左单旋、右单旋、左右双旋、右左双旋各自是什么意思呢?请继续往下看 ~(^ ^)~
🌟左单旋
当插入的节点导致 右子树的高度 > 左子树的高度 时,我们需要进行旋转,让树保持左右平衡。
在图中,a、b、c 分别代表一颗高度相同的子树(画的是抽象图),此处,我们讨论的是当节点插入在c子树时的情况,此时就会导致右边树的高度大于左边树的高度,为此,我们需要进行旋转,即:
• parent的右指向subRL (此时我们要注意判断subRL是否存在);
• subR的左指向parent;
• 要判断parent是否为root,不为根我们需要进行判断parent是parentParent的左边还是右边;
• 旋转完后,重新给parent和subR的平衡因子赋值;
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
//处理 parent 和 subRL 的关系
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
//处理 parent 和 subR 的关系
subR->_left = parent;
parent->_parent = subR;
//判断parent是否为根?
if (parentParent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)//不为根,判断是根的左边还是右边
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
parent->_bf = subR->_bf = 0;//旋转平衡后,更新平衡因子
}
🌟右单旋
与左单旋同理:
此时在a子树插入,导致 左子树的高度 > 右子树的高度 ,进行右旋保持书的平衡。
• parent的左指向subLR (此时我们要注意判断subLR是否存在);
• subL的右指向parent;
• 要判断parent是否为root,不为根我们需要进行判断parent是parentParent的左边还是右边;
• 旋转完后,重新给parent和subL的平衡因子赋值;
// 右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//处理 parent 和 subLR 的关系
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
//处理 parent 和 subL 的关系
subL->_right = parent;
parent->_parent = subL;
//判断是否为根?
if (parentParent==nullptr)//为根
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)//不为根,判断是根的左还是右
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
parent->_bf = subL->_bf = 0;//旋转平衡后,更新平衡因子
}
🌟左右双旋
为什么还有左右双旋?从上面的左单旋、右单旋,我们可以知道,插入的节点都是偏向同一边的(left->left、right->right),如果我们插入的节点不在同一侧呢?(left->right、right->left)
当我们遇到下面这种情况时,仅仅单旋一次是不能解决问题的:
为此我们需要双旋:
双旋分为两种情况:h == 0 和 h > 0 的情况。
• 我们可以复用上面写过的左单旋和右单旋;
• 插入后,关键看subLR的平衡因子区分;
• 进行旋转完后,最重要的是维护好平衡因子,情况一和情况二的区别就是旋转后平衡因子的变化不一样。
// 左右双旋
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 0) // 情况一
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1) //情况二:c 插入
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1) // 情况二:b 插入
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else
{
assert(false);// 不是以上的情况就表明树本身就有问题
}
}
🌟右左双旋
与左右双旋同理:
• 我们可以复用上面写过的左单旋和右单旋;
• 插入后,关键看subRL的平衡因子区分;
• 进行旋转完后,最重要的是维护好平衡因子,情况一和情况二的区别就是旋转后平衡因子的变化不一样。
// 右左双旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 0) //情况一:h == 0
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1) // 情况二:h > 0 c插入
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1) // 情况二:h > 0 b插入
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
🌟AVL树的验证
• 分别统计左子树的高度和右子树的高度;
• 右子树的高度 — 左子树的高度 < 2 ;
• 平衡因子等于高度差。
// AVL树的验证
bool IsAVLTree()
{
return _IsAVLTree(_root);
}
int Height()
{
return _Height(_root);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
int Size()
{
return _Size(_root);
}
private:
int _Size(Node* root)
{
//左边+右边
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
// 根据AVL树的概念验证pRoot是否为有效的AVL树
bool _IsAVLTree(Node* root)
{
if (nullptr == root)
{
return true;
}
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
if (abs(diff) >= 2)
{
cout << root->_kv.first << "高度差出现异常" << endl;
}
if (root->_bf != diff)
{
cout << root->_kv.first << "平衡因子出现异常" << endl;
}
return _IsAVLTree(root->_left) && _IsAVLTree(root->_right);
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
三、完整代码
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
template<class K,class V>
struct AVLTreeNode
{
pair<K , V> _kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;
AVLTreeNode(const pair<K ,V>& kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
{}
};
template<class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree() = default;
// 在AVL树中插入值为kv的节点
bool Insert(const pair<K,V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;//记录parent
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
//找到相同的数,返回false
return false;
}
}
//插入新节点
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;//与parent链接
//更新平衡因子
while (parent)
{
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)
{
RotateLR(parent);
}
else
{
RotateRL(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
// AVL树的验证
bool IsAVLTree()
{
return _IsAVLTree(_root);
}
int Height()
{
return _Height(_root);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
int Size()
{
return _Size(_root);
}
private:
int _Size(Node* root)
{
//左边+右边
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
// 根据AVL树的概念验证pRoot是否为有效的AVL树
bool _IsAVLTree(Node* root)
{
if (nullptr == root)
{
return true;
}
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
if (abs(diff) >= 2)
{
cout << root->_kv.first << "高度差出现异常" << endl;
}
if (root->_bf != diff)
{
cout << root->_kv.first << "平衡因子出现异常" << endl;
}
return _IsAVLTree(root->_left) && _IsAVLTree(root->_right);
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
// 右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
//判断是否为根?
if (parentParent==nullptr)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
parent->_bf = subL->_bf = 0;
}
// 左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parentParent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
parent->_bf = subR->_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)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
subRL->_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 == 0)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else
{
assert(false);
}
}
private:
Node* _root = nullptr;
};
void AVLTreetest1()
{
AVLTree<int, int> t;
int a[] = { 16,3,7,11,9,26,18,14,15 };
//int a[] = { 4,2,6,1,3,5,15,7,16,14 };
for (auto e : a)
{
/* if (e == 11)
{
int i = 0;
}*/
t.Insert({ e,e });
//cout << e << "->" << t._IsBalanceTree() << endl;
}
t.InOrder();
cout << t.IsAVLTree();
}
void AVLTreetest2()
{
const int N = 100000;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
{
v.push_back(rand());
//cout << v.back() << endl;
}
size_t begin2 = clock();
AVLTree<int, int> t;
for (auto e : v)
{
t.Insert(make_pair(e, e));
//cout << "Insert:" << e << "->" << t.IsBalance() << endl;
}
size_t end2 = clock();
cout << "Insert:" << end2 - begin2 << endl;
//cout << t.IsBalance() << endl;
cout << "Height:" << t.Height() << endl;
cout << "Size:" << t.Size() << endl;
size_t begin1 = clock();
// 确定在的值
for (auto e : v)
{
t.Find(e);
}
// 随机值
/*for (size_t i = 0; i < N; i++)
{
t.Find((rand() + i));
}*/
size_t end1 = clock();
cout << "Find:" << end1 - begin1 << endl;
}
如若对你有帮助,记得点赞、收藏、关注哦!
若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢^ ^ ~