C++ AVL树实现详解:理论+代码+图解
C++ AVL树实现详解:理论+代码+图解
文章目录
- C++ AVL树实现详解:理论+代码+图解
- 一、AVL的概念
- 二、AVL树的实现
- 2.1 AVL的结构
- 2.2 AVL树的插入
- 2.2.1 AVL树插入一个值的大概过程
- 2.2.2 平衡因子更新
- 2.2.3 插入结点及更新平衡因子的代码实现
- 2.3 旋转
- 2.3.1 旋转的原则
- 2.3.2 左旋
- 2.3.3 右旋
- 2.4 旋转的四种情况
- 2.4.1 LL型
- 2.4.2 RR型
- 2.4.3 LR型
- 2.4.4 RL型
- 2.5 AVL树插入的注意事项
- 2.6 旋转的代码实现
- 2.6.1 左单旋代码实现(LL)
- 2.6.2 右单旋代码实现(RR)
- 2.6.3 左右双旋代码实现(LR)
- 2.6.4 右左双旋代码实现(RL)
- 2.7 AVL树的查找
- 2.8 AVL树平衡检测
- 2.8.1 AVL树的高度
- 2.8.2 AVL树的平衡
- 2.9 AVL树删除
- 三、整体源代码
一、AVL的概念
二、AVL树的实现
AVL树查找,插入,构建,删除的过程和二叉搜索树一致,只是失衡的时候需要调整(旋转)
2.1 AVL的结构
这里和二叉搜索树基本一致,多了一个平衡因子
2.2 AVL树的插入
2.2.1 AVL树插入一个值的大概过程
2.2.2 平衡因子更新
2.2.3 插入结点及更新平衡因子的代码实现
代码如下(示例):
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){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// 更新平衡因⼦while (parent){// 更新平衡因⼦if (cur == parent->_left)parent->_bf--;elseparent->_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){// 不平衡了,旋转处理break;}else{assert(false);}}return true;}
2.3 旋转
2.3.1 旋转的原则
这里的平衡因子我们统一用左子树高度-右子树高度进行计算
2.3.2 左旋
2.3.3 右旋
2.4 旋转的四种情况
2.4.1 LL型
2.4.2 RR型
2.4.3 LR型
2.4.4 RL型
2.5 AVL树插入的注意事项
注意:这里只是插入这样做,删除不是这样做的
2.6 旋转的代码实现
2.6.1 左单旋代码实现(LL)
代码如下(示例):
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;
}
2.6.2 右单旋代码实现(RR)
代码如下(示例):
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;// parent有可能是整棵树的根,也可能是局部的⼦树// 如果是整棵树的根,要修改_root// 如果是局部的指针要跟上⼀层链接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;}
左右双旋和右左双旋,主要是在函数里面调用 左单旋和右单旋
2.6.3 左右双旋代码实现(LR)
代码如下(示例):
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 = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else if(bf == 1)
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
2.6.4 右左双旋代码实现(RL)
代码如下(示例):
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);
}
}
2.7 AVL树的查找
按照二叉搜索树逻辑实现即可,搜索效率为O(logN)
代码如下(示例):
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;}
2.8 AVL树平衡检测
我们实现的AVL树是否合格,我们通过检查左右子树高度差的程序进行反向验证,同时检查一下节点的平衡因子更新是否出现了问题!
2.8.1 AVL树的高度
这里采用递归的方式,与二叉树求根的高度方法一致
代码如下(示例):
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;}
2.8.2 AVL树的平衡
这里同样也采用了递归的方法进行检测
代码如下(示例):
bool _IsBalanceTree(Node* root){// 空树也是AVL树if (nullptr == root)return true;// 计算pRoot结点的平衡因⼦:即pRoot左右⼦树的⾼度差int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;// 如果计算出的平衡因⼦与pRoot的平衡因⼦不相等,或者// pRoot平衡因⼦的绝对值超过1,则⼀定不是AVL树if (abs(diff) >= 2){cout << root->_kv.first << "⾼度差异常" << endl;return false;}if (root->_bf != diff){cout << root->_kv.first << "平衡因⼦异常" << endl;return false;}// pRoot的左和右如果都是AVL树,则该树⼀定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);}
2.9 AVL树删除
代码如下(示例):
void erase(const K& val){//递归删除_root = _erase(_root, val);}Node* _erase(Node* node, const K& val){// 如果当前节点为空,直接返回空指针if (node == nullptr){return nullptr;}// 如果当前节点的键值大于要删除的键值,在左子树中删除if (node->_kv.first > val){node->_left = _erase(node->_left, val);//更新节点的父节点if (node->_left)node->_left->_parent = node;// 调整节点的平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);int bf = node->_bf;//情况一删除节点都在左边if (bf == 2){//右侧的平衡因子int right_bf = node->_right->_bf;if (right_bf >= 0){Node* subR = node->_right;Node* parent = node;RotateL(node);//高度相等,要重新更新平衡因子if (right_bf == 0){subR->_bf = -1;parent->_bf = 1;}}else{RotateRL(node);}}}// 如果当前节点的键值小于要删除的键值,在右子树中删除else if (node->_kv.first < val){node->_right = _erase(node->_right, val);//更新节点的父节点if (node->_right)node->_right->_parent = node;// 调整节点平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);int bf = node->_bf;//情况二删除节点都在右边if (bf == -2){//左侧的平衡因子int left_bf = node->_left->_bf;if (left_bf <= 0){Node* subL = node->_left;Node* parent = node;RotateR(node);//高度相等,要重新更新平衡因子if (left_bf == 0){subL->_bf = 1;parent->_bf = -1;}}else{RotateLR(node);}}}// 找到要删除的节点else{// 如果节点有两个孩子if (node->_left != nullptr && node->_right != nullptr){// 如果左子树高度大于等于右子树高度if (Hegiht(node->_left) >= Hegiht(node->_right)){// 找到左子树中的最大节点Node* prev = node->_left;while (prev->_right)prev = prev->_right;int target = prev->_kv.first;// 将当前节点的值替换为左子树中的最大节点的值node->_kv = prev->_kv;// 在左子树中删除该最大节点node->_left = _erase(node->_left, target);if (node->_left)node->_left->_parent = node;// 调整节点的平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);}else{// 找到右子树中的最小节点Node* post = node->_right;while (post->_left)post = post->_left;// 将当前节点的值替换为右子树中的最小节点的值int target = post->_kv.first;node->_kv = post->_kv;// 在右子树中删除该最小节点node->_right = _erase(node->_right, target);if (node->_right)node->_right->_parent = node;// 调整节点平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);}}// 如果节点最多有一个孩子else{// 如果有左孩子if (node->_left != nullptr){// 保存左孩子指针Node* left = node->_left;// 删除当前节点delete node;// 返回左孩子指针return left;}// 如果有右孩子else if (node->_right != nullptr){// 保存右孩子指针Node* right = node->_right;// 删除当前节点delete node;// 返回右孩子指针return right;}// 如果没有孩子else{// 返回空指针delete node;return nullptr;}}}// 返回调整后的节点指针return node;}
三、整体源代码
代码如下(示例):
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{// 需要parent指针,后续更新平衡因⼦可以看到pair<K, V> _kv;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf; // balance factorAVLTreeNode(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://...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){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// 更新平衡因⼦while (parent){// 更新平衡因⼦if (cur == parent->_left)parent->_bf--;elseparent->_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){// 不平衡了,旋转处理break;}else{assert(false);}}return true;}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 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;// parent有可能是整棵树的根,也可能是局部的⼦树// 如果是整棵树的根,要修改_root// 如果是局部的指针要跟上⼀层链接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 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 = 0;subLR->_bf = 0;parent->_bf = 1;}else if (bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else{assert(false);}}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);}}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;}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;}bool _IsBalanceTree(Node* root){// 空树也是AVL树if (nullptr == root)return true;// 计算pRoot结点的平衡因⼦:即pRoot左右⼦树的⾼度差int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;// 如果计算出的平衡因⼦与pRoot的平衡因⼦不相等,或者// pRoot平衡因⼦的绝对值超过1,则⼀定不是AVL树if (abs(diff) >= 2){cout << root->_kv.first << "⾼度差异常" << endl;return false;}if (root->_bf != diff){cout << root->_kv.first << "平衡因⼦异常" << endl;return false;}// pRoot的左和右如果都是AVL树,则该树⼀定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);}// 测试代码void TestAVLTree1(){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){t.Insert({ e, e });}t.InOrder();cout << t.IsBalanceTree() << endl;}// 插⼊⼀堆随机值,测试平衡,顺便测试⼀下⾼度和性能等void TestAVLTree2(){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() + i);}ize_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 << t.IsBalanceTree() << 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;}void erase(const K& val){//递归删除_root = _erase(_root, val);}Node* _erase(Node* node, const K& val){// 如果当前节点为空,直接返回空指针if (node == nullptr){return nullptr;}// 如果当前节点的键值大于要删除的键值,在左子树中删除if (node->_kv.first > val){node->_left = _erase(node->_left, val);//更新节点的父节点if (node->_left)node->_left->_parent = node;// 调整节点的平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);int bf = node->_bf;//情况一删除节点都在左边if (bf == 2){//右侧的平衡因子int right_bf = node->_right->_bf;if (right_bf >= 0){Node* subR = node->_right;Node* parent = node;RotateL(node);//高度相等,要重新更新平衡因子if (right_bf == 0){subR->_bf = -1;parent->_bf = 1;}}else{RotateRL(node);}}}// 如果当前节点的键值小于要删除的键值,在右子树中删除else if (node->_kv.first < val){node->_right = _erase(node->_right, val);//更新节点的父节点if (node->_right)node->_right->_parent = node;// 调整节点平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);int bf = node->_bf;//情况二删除节点都在右边if (bf == -2){//左侧的平衡因子int left_bf = node->_left->_bf;if (left_bf <= 0){Node* subL = node->_left;Node* parent = node;RotateR(node);//高度相等,要重新更新平衡因子if (left_bf == 0){subL->_bf = 1;parent->_bf = -1;}}else{RotateLR(node);}}}// 找到要删除的节点else{// 如果节点有两个孩子if (node->_left != nullptr && node->_right != nullptr){// 如果左子树高度大于等于右子树高度if (Hegiht(node->_left) >= Hegiht(node->_right)){// 找到左子树中的最大节点Node* prev = node->_left;while (prev->_right)prev = prev->_right;int target = prev->_kv.first;// 将当前节点的值替换为左子树中的最大节点的值node->_kv = prev->_kv;// 在左子树中删除该最大节点node->_left = _erase(node->_left, target);if (node->_left)node->_left->_parent = node;// 调整节点的平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);}else{// 找到右子树中的最小节点Node* post = node->_right;while (post->_left)post = post->_left;// 将当前节点的值替换为右子树中的最小节点的值int target = post->_kv.first;node->_kv = post->_kv;// 在右子树中删除该最小节点node->_right = _erase(node->_right, target);if (node->_right)node->_right->_parent = node;// 调整节点平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);}}// 如果节点最多有一个孩子else{// 如果有左孩子if (node->_left != nullptr){// 保存左孩子指针Node* left = node->_left;// 删除当前节点delete node;// 返回左孩子指针return left;}// 如果有右孩子else if (node->_right != nullptr){// 保存右孩子指针Node* right = node->_right;// 删除当前节点delete node;// 返回右孩子指针return right;}// 如果没有孩子else{// 返回空指针delete node;return nullptr;}}}// 返回调整后的节点指针return node;}private:Node* _root = nullptr;
};