【高阶数据结构】AVL树
目录
- 1. 什么是 AVL 树?
- 2. 为什么需要 AVL 树?
- 3. AVL树的模拟实现
- 3.1 AVL树的定义
- 3.2 AVL树的插入
- 3.2.1 情况 1:右右情况(RR)(2,1) -> 左单旋
- 3.2.2 情况 2:左左情况(LL)(-2,-1) -> 右单旋
- 3.2.3 情况 3:右左情况(RL)(2,-1) -> 右左双旋(先右旋再左旋)
- 3.2.4 情况 4:左右情况(LR)(-2,1) -> 左右双旋(先左旋再右旋)
 
- 3.3 AVL树的求高以及平衡的判断
 
- 4. 源代码
1. 什么是 AVL 树?
简单来说,AVL 树是一种自平衡的二叉搜索树(BST)。
它是由两位苏联数学家 G. M. Adelson-Velsky 和 E. M. Landis 在 1962 年发明的,AVL 这个名字就来源于他们姓氏的首字母。
核心思想:在二叉搜索树的基础上,增加了一个平衡条件——对于树中的任意一个节点,其左子树和右子树的高度差不能超过 1。
这个“高度差”有一个专门的名字,叫做 平衡因子。
平衡因子 = 右子树高度 - 左子树高度
平衡因子的值只能是 -1, 0, 1 中的一个。如果出现 2 或 -2,就意味着树不平衡了,需要通过“旋转”来修复。
2. 为什么需要 AVL 树?
普通的二叉搜索树(BST)有一个致命缺点:它的性能严重依赖于插入的顺序。
最佳情况:插入顺序得当,树是完全平衡的,搜索、插入、删除的时间复杂度都是 O(log n)。
最坏情况:如果插入的数据是有序的(例如 1, 2, 3, 4, 5…),BST 会退化成一条链表,时间复杂度变为 O(n)。
AVL 树就是为了解决这个问题而生的。它通过强制维持树的平衡,保证了在最坏情况下,所有操作的时间复杂度仍然是 O(log n)。这个“维持平衡”的过程,就是我们下面要讲的 旋转。
3. AVL树的模拟实现
3.1 AVL树的定义
#pragma once
#include <iostream>
#include <assert.h>using namespace std;template<typename K, typename 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<typename K, typename V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:AVLTree():_root(nullptr){}private:Node* _root;
};
3.2 AVL树的插入
关键步骤:
- 找到节点的位置
- 生成一个节点,并把节点连接好
- 以此节点的位置开始更新 -  - 把当前的parent节点平衡因子更新一下(+1或-1)
 
-  - 若parent节点平衡因子为0,则该树已经平衡,插入已经完成
 
-  - 若parent节点平衡因子为-1或者1,则继续更迭parent,cur,向上更新
 
-  - 若parent节点平衡因子为-2或者2,需要进行旋转,一次旋转(无论哪个旋转),都可以让parent的平衡因子直接变成0,即完成了插入。
 
 
-  
bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);return true;}else{Node* parent = nullptr;Node* cur = _root;//1. 找到节点的位置while (cur){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;}}//2. 生成一个节点,并把节点连接好cur = new Node(kv);cur->_parent = parent;//三叉链需要额外链接cur的父亲节点if (cur->_kv.first > parent->_kv.first){parent->_right = cur;}else{parent->_left = cur;}//3. 以此节点的位置开始更新while (parent)//当parent == nullptr的时候,无法继续向上更新,退出即可{//更新parent的平衡因子if (cur->_kv.first > parent->_kv.first){parent->_bf++;}else{parent->_bf--;}if (parent->_bf == 0)//此时父亲节点的平衡因子为0,退出即可{break;}else if (parent->_bf == 1 || parent->_bf == -1)//迭代cur和parent,继续{											   //向上更新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//如果cur的平衡因子不符合为1或-1的情况,说明在插入前树已经不是AVL树了{   //直接断言错误即可assert(false);}break;}else//如果parent的平衡因子不符合为0,1或-1,2或-2的情况{   //说明在插入前树已经不是AVL树了,直接断言错误即可assert(false);}}}return true;
}
接下来,我们讲旋转,讲旋转前,要知道的事情。
 当 parent 和 cur 的平衡因子确定时,他们的位置也就确定了。
 我们讲的左单璇,parent->_bf == 2 && cur->_bf == 1,即(2,1)的情况。那么简洁操作图解一定是:
    A (平衡因子=2) parent\B (平衡因子=1) cur\C (新插入)
不可能是:
        A (平衡因子=2) parent/B (平衡因子=1)  cur/C (新插入)
因为parent平衡因子为2,那么右子树一定出现了问题,那么cur一定出现在右边,因为cur是从插入点一步步更新过来的。
3.2.1 情况 1:右右情况(RR)(2,1) -> 左单旋
parent的平衡因子为 2 ,cur的平衡因子为 1 时,是需要使用左单旋的情况。
 左单璇后,parent、cur 的平衡因子都变成 0
 
 代码解析:
void RotateL(Node* parent)
{ode* ppnode = parent->_parent;ode* cur = parent->_right; Node* curleft = cur->_left;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{cur->_parent = ppnode;if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}}parent->_bf = cur->_bf = 0;
}
3.2.2 情况 2:左左情况(LL)(-2,-1) -> 右单旋
parent的平衡因子为 -2 ,cur的平衡因子为 -1 时,是需要使用右单旋的情况。
 右单璇后,parent、cur 的平衡因子都变成 0
 
 代码解析:
void RotateR(Node* parent)
{Node* ppnode = parent->_parent;Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright){curright->_parent = parent;}cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;cur->_parent = ppnode;}else{ppnode->_right = cur;cur->_parent = ppnode;}}parent->_bf = cur->_bf = 0;
}
3.2.3 情况 3:右左情况(RL)(2,-1) -> 右左双旋(先右旋再左旋)
parent的平衡因子为 2 ,cur的平衡因子为 -1 时,是需要使用右左单旋的情况。
 右左单璇后,parent、cur 的平衡因子不像左旋和右璇一样都变成0。而是有不同情况。

 总结:
| 旋转前的curleft平衡因子 | 旋转后的parent平衡因子 | 旋转后的cur平衡因子 | 旋转后的curleft平衡因子 | |
|---|---|---|---|---|
| 第一种情况 | 1 | -1 | 0 | 0 | 
| 第二种情况 | -1 | 1 | 0 | 0 | 
| 第三种情况 | 0 | 0 | 0 | 0 | 
代码如下:
void RotateRL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;RotateR(cur);RotateL(parent);if (bf == 0)//此时对应h == 0的情况,尽管我们调用的左单旋和右单旋中已经对parent和cur{           //和curleft的平衡因子进行了无脑置0,但是这里为了解耦,即降低耦合性,所以parent->_bf = 0;//我们需要手动置0,降低耦合性cur->_bf = 0;curleft->_bf = 0;}else if (bf == -1){parent->_bf = 0;cur->_bf = 1;curleft->_bf = 0;}else if (bf == 1){parent->_bf = -1;cur->_bf = 0;curleft->_bf = 0;}else//如果bf出现其它情况,那么插入前该树已经不是AVL树,所以直接断言false即可{assert(false);}
}3.2.4 情况 4:左右情况(LR)(-2,1) -> 左右双旋(先左旋再右旋)
parent的平衡因子为 -2 ,cur的平衡因子为 1 时,是需要使用左右单旋的情况。
 左右单璇后,parent、cur 的平衡因子不像左旋和右璇一样都变成0。而是有不同情况。
 其实就是和右左双旋同理。
 
总结:
| 旋转前的curright平衡因子 | 旋转后的parent平衡因子 | 旋转后的cur平衡因子 | 旋转后的curright平衡因子 | |
|---|---|---|---|---|
| 第一种情况 | 1 | 0 | -1 | 0 | 
| 第二种情况 | -1 | 1 | 0 | 0 | 
| 第三种情况 | 0 | 0 | 0 | 0 | 
代码如下:
void RotateLR(Node* parent)
{Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;RotateL(cur);RotateR(parent);if (bf == 0){parent->_bf = 0;cur->_bf = 0;curright->_bf = 0;}else if (bf == -1){parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1){parent->_bf = 0;cur->_bf = -1;curright->_bf = 0;}else{assert(false);}
}
3.3 AVL树的求高以及平衡的判断
代码如下:
int Height()
{return Height(_root);
}int Height(Node* root)
{if (root == nullptr){return 0;}int heightL = Height(root->_left);int heightR = Height(root->_right);return  heightL > heightR ? heightL + 1 : heightR + 1;
}bool Isbalance()
{return Isbalance(_root);
}bool Isbalance(Node* root)
{if (root == nullptr){return true;}Node* left = root->_left;Node* right = root->_right;int heightL = Height(left);int heightR = Height(right);int bf = heightR - heightL;if (bf != root->_bf){cout << "平衡因子异常" << root->_kv.first << ':' << root->_bf << endl;return false;}return abs(bf) <= 1 && Isbalance(left) && Isbalance(right);
}4. 源代码
AVLTree.h:
#pragma once
#include <iostream>
#include <assert.h>using namespace std;template<typename K, typename 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<typename K, typename V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:AVLTree():_root(nullptr){}bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}else{Node* parent = nullptr;Node* cur = _root;while (cur){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);cur->_parent = parent;if (cur->_kv.first > parent->_kv.first){parent->_right = cur;}else{parent->_left = cur;}while (parent){if (cur->_kv.first > parent->_kv.first){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;}int Height(){return Height(_root);}int Height(Node* root){if (root == nullptr){return 0;}int heightL = Height(root->_left);int heightR = Height(root->_right);return  heightL > heightR ? heightL + 1 : heightR + 1;}bool Isbalance(){return Isbalance(_root);}bool Isbalance(Node* root){if (root == nullptr){return true;}Node* left = root->_left;Node* right = root->_right;int heightL = Height(left);int heightR = Height(right);int bf = heightR - heightL;if (bf != root->_bf){cout << "平衡因子异常" << root->_kv.first << ':' << root->_bf << endl;return false;}return abs(bf) <= 1 && Isbalance(left) && Isbalance(right);}private:void RotateL(Node* parent){Node* ppnode = parent->_parent;Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{cur->_parent = ppnode;if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}}parent->_bf = cur->_bf = 0;}void RotateR(Node* parent){Node* ppnode = parent->_parent;Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright){curright->_parent = parent;}cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;cur->_parent = ppnode;}else{ppnode->_right = cur;cur->_parent = ppnode;}}parent->_bf = cur->_bf = 0;}void RotateRL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;RotateR(cur);RotateL(parent);if (bf == 0){parent->_bf = 0;cur->_bf = 0;curleft->_bf = 0;}else if (bf == -1){parent->_bf = 0;cur->_bf = 1;curleft->_bf = 0;}else if (bf == 1){parent->_bf = -1;cur->_bf = 0;curleft->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;RotateL(cur);RotateR(parent);if (bf == 0){parent->_bf = 0;cur->_bf = 0;curright->_bf = 0;}else if (bf == -1){parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1){parent->_bf = 0;cur->_bf = -1;curright->_bf = 0;}else{assert(false);}}private:Node* _root;
};
