目录
什么是AVL树
首先还是介绍一下什么是AVL树
AVL树是一种自平衡二叉搜索树,它的每个节点都保存一个平衡因子(balance factor),用于判断是否需要进行旋转操作来保持树的平衡。平衡因子是右子树的高度减去左子树的高度(有时相反),因此它的值只可能是-1、0或1。如果插入或删除一个节点后,某个节点的平衡因子的绝对值大于1,就需要进行旋转操作来重新平衡这棵树。
AVL树的插入、删除和查找操作的时间复杂度都是O(log n),其中n是树中节点的数量。AVL树的性能比普通的二叉搜索树更稳定,因为它保证了树的高度始终是O(log n)级别的。
AVL树的结构
通过之前二叉搜索树和set与map的学习,我们传入pair这个键值对,然后由于AVL树的特殊操作,我们也需要通过三叉链来实现
template<class K, class V>
struct AVLTreeNode {
// 三叉链结构
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
AVLTreeNode(const pair<K,V>& kv)
: _left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
// 键值对
pair<K, V> _kv;
// balance fator
int _bf;
};
template<class K, class V>
class AVLTree {
typedef AVLTreeNode<K, V> Node;
private:
Node* _root;
};
AVL树的增删查改
增
插入部分就跟讲的搜索二叉树插入一致
bool insert(const pair<K,V>& kv) {
if (_root == nullptr) {
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* current = _root;
while(current != nullptr) {
parent = current;
if (current->_kv.first > kv.first)
current = current->_left;
else if (current->_kv.first < kv.first)
current = current->_right;
else
return false;
}
current = new Node(kv);
// 判断current在parrent的左还是右
if (parent->_kv.first < kv.first) {
parent->_right = current;
// parent赋值给current的父指针
current->_parent = parent;
}
else {
parent->_left = current;
current->_parent = parent;
}
return true;
}
我们从AVL树的定义知道我们在插入节点的时候需要控制这棵树的平衡程度,所以我们不仅要实现,插入,还得在插入后来判断是否平衡,以及平衡后如何旋转
如图为AVL的平衡情况判断
// 完成插入后开始更新平衡因子_bf
while (parent!=nullptr) { // 能不能current != _root
// 插入后如果在父节点左边
if (current == parent->_left)
parent->_bf--;
else
parent->_bf++;
// 如果父节点为0,跳出循环不再调整
if (parent->_bf == 0) {
break;
}
// 如果父节点不为0,那么往上调整父节点的父节点
else if (parent->_bf == 1 || parent->_bf == -1) {
// 更新current
current = parent;
// 更新parent
parent = parent->_parent;
}
// 为-2 2时需要旋转
else if(parent->_bf == 2 || parent->_bf == -2){
// 单旋转的场景
if (parent->_bf == 2 && current->_bf == 1) {
rotateL(parent);
}
else if (parent->_bf == -2 && current->_bf == -1) {
rotateR(parent);
}
// 双旋转的场景
else if (parent->_bf == 2 && current->_bf == -1) {
rotateRL(parent);
}
else if (parent->_bf == -2 && current->_bf == 1) {
rotateLR(parent);
}
// 只要通过了旋转 就已经实现了平衡因子
break;
}
else {
cout << " 代码有误" << endl;
assert(false);
}
如何实现AVL树的旋转呢?我们先试着分析一下
首先我们先探讨一下平衡后的子树,需不需要向上更新平衡因子?
注意各部分分析,需要结合代码与图像! 这一部分真的好难 红黑树怎么办啊
情况一:
较高的子树为最右子树
// 左单旋转
void rotateL(Node* root) {
Node* parent = root;
// 相对根的右边
Node* subR = root->_right;
// 相对根的右边的左边
Node* subRL = subR->_left;
Node* GrandParent = parent->_parent;
// 旋转后的相对位置
subR->_left = parent;
parent->_right = subRL;
if (subRL != nullptr)
subRL->_parent = parent;
// else时为空指针,空指针走->_parent会报错
parent->_parent = subR;
// 将subR设为新的父指针
parent = subR;
// 传入的root一定是 _root 吗
if (GrandParent == nullptr) {
// 是根的话 就需要将parent的位置设为_root
_root = parent;
parent->_parent = GrandParent;
}
else {
// 传入的为子树的根所以grandparent不为空
if (GrandParent->_left == root)
// 不是根的话就需要把grandpa的左右指向parent
GrandParent->_left = parent;
else
GrandParent->_right = parent;
// 实现连接
parent->_parent = GrandParent;
}
parent->_bf = parent->_left->_bf = 0;
}
同理这右单旋转也几乎一致
情况二:
较高子树为最左子树
// 右单旋转
void rotateR(Node* root) {
Node* parent = root;
// 注释参照左旋转
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* GrandParent = parent->_parent;
subL->_right = parent;
parent->_left = subLR;
if (subLR != nullptr)
subLR->_parent = parent;
parent->_parent = subL;
parent = subL;
if (GrandParent == nullptr) {
_root = parent;
parent->_parent = GrandParent;
}
else {
if (GrandParent->_left == root)
GrandParent->_left = parent;
else
GrandParent->_right = parent;
parent->_parent = GrandParent;
}
parent->_bf = parent->_right->_bf = 0;
}
情况三:
较高子树为右子树的左子树
// 双旋 右+左
void rotateRL(Node* root) {
Node* parent = root;
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
// 因为对于parent来说,右树高度过高,需要调整
// 相对root->right 这个节点左树过高需要右旋
rotateR(root->_right);
// 右旋后相对parent来说 右树高度过高需要左旋
rotateL(root);
// 通过 subRL 平衡因子来区分多种情况
if (bf == 0) {
// 插入的节点恰好为subRL
subR->_bf = parent->_bf = subRL->_bf = 0;
}
else if (bf == -1) {
// 往subRL的左树插入
parent->_bf = subRL->_bf = 0;
subR->_bf = 1;
}
else if (bf == 1) {
// 往subRL的右树插入
subRL->_bf = subR->_bf = 0;
parent->_bf = -1;
}
else
assert(false);
}
情况四:
较高子树为左子树的右子树
// 双旋 左+右
void rotateLR(Node* root) {
Node* parent = root;
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
rotateL(root->_left);
rotateR(root);
if (bf == 0) {
subL->_bf = subLR->_bf = parent->_bf = 0;
}
else if (bf == -1) {
parent->_bf = 1;
subL->_bf = subLR->_bf = 0;
}
else if (bf == 1) {
subL->_bf = -1;
parent->_bf = subLR->_bf = 0;
}
else
assert(false);
}
到了这里insert的内容就完成了!这个模块的最终代码
bool insert(const pair<K,V>& kv) {
if (_root == nullptr) {
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* current = _root;
while(current != nullptr) {
parent = current;
if (current->_kv.first > kv.first)
current = current->_left;
else if (current->_kv.first < kv.first)
current = current->_right;
else
return false;
}
current = new Node(kv);
// 判断current在parrent的左还是右
if (parent->_kv.first < kv.first) {
parent->_right = current;
// parent赋值给current的父指针
current->_parent = parent;
}
else {
parent->_left = current;
current->_parent = parent;
}
// 完成插入后开始更新平衡因子_bf
while (parent!=nullptr) { // 能不能current != _root
// 插入后如果在父节点左边
if (current == parent->_left)
parent->_bf--;
else
parent->_bf++;
// 如果父节点为0,跳出循环不再调整
if (parent->_bf == 0) {
break;
}
// 如果父节点不为0,那么往上调整父节点的父节点
else if (parent->_bf == 1 || parent->_bf == -1) {
// 更新current
current = parent;
// 更新parent
parent = parent->_parent;
}
// 为-2 2时需要旋转
else if(parent->_bf == 2 || parent->_bf == -2){
// 单旋转的场景
if (parent->_bf == 2 && current->_bf == 1) {
rotateL(parent);
}
else if (parent->_bf == -2 && current->_bf == -1) {
rotateR(parent);
}
// 双旋转的场景
else if (parent->_bf == 2 && current->_bf == -1) {
rotateRL(parent);
}
else if (parent->_bf == -2 && current->_bf == 1) {
rotateLR(parent);
}
// 只要通过了旋转 就已经实现了平衡因子
break;
}
else {
cout << " 代码有误" << endl;
assert(false);
}
}
return true;
}
// 左单旋转
void rotateL(Node* root) {
Node* parent = root;
// 相对根的右边
Node* subR = root->_right;
// 相对根的右边的左边
Node* subRL = subR->_left;
Node* GrandParent = parent->_parent;
// 旋转后的相对位置
subR->_left = parent;
parent->_right = subRL;
if (subRL != nullptr)
subRL->_parent = parent;
// else时为空指针,空指针走->_parent会报错
parent->_parent = subR;
// 将subR设为新的父指针
parent = subR;
// 传入的root一定是 _root 吗
if (GrandParent == nullptr) {
_root = parent;
parent->_parent = GrandParent;
}
else {
// 传入的为子树的根所以grandparent不为空
if (GrandParent->_left == root)
GrandParent->_left = parent;
else
GrandParent->_right = parent;
// 实现连接
parent->_parent = GrandParent;
}
parent->_bf = parent->_left->_bf = 0;
}
// 右单旋转
void rotateR(Node* root) {
Node* parent = root;
// 注释参照左旋转
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* GrandParent = parent->_parent;
subL->_right = parent;
parent->_left = subLR;
if (subLR != nullptr)
subLR->_parent = parent;
parent->_parent = subL;
parent = subL;
if (GrandParent == nullptr) {
_root = parent;
parent->_parent = GrandParent;
}
else {
if (GrandParent->_left == root)
GrandParent->_left = parent;
else
GrandParent->_right = parent;
parent->_parent = GrandParent;
}
parent->_bf = parent->_right->_bf = 0;
}
// 双旋 右+左
void rotateRL(Node* root) {
Node* parent = root;
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
// 因为对于parent来说,右树高度过高,需要调整
// 相对root->right 这个节点左树过高需要右旋
rotateR(root->_right);
// 右旋后相对parent来说 右树高度过高需要左旋
rotateL(root);
// 通过 subRL 平衡因子来区分多种情况
if (bf == 0) {
// 插入的节点恰好为subRL
subR->_bf = parent->_bf = subRL->_bf = 0;
}
else if (bf == -1) {
// 往subRL的左树插入
parent->_bf = subRL->_bf = 0;
subR->_bf = 1;
}
else if (bf == 1) {
// 往subRL的右树插入
subRL->_bf = subR->_bf = 0;
parent->_bf = -1;
}
else
assert(false);
}
// 双旋 左+右
void rotateLR(Node* root) {
Node* parent = root;
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
rotateL(root->_left);
rotateR(root);
if (bf == 0) {
subL->_bf = subLR->_bf = parent->_bf = 0;
}
else if (bf == -1) {
parent->_bf = 1;
subL->_bf = subLR->_bf = 0;
}
else if (bf == 1) {
subL->_bf = -1;
parent->_bf = subLR->_bf = 0;
}
else
assert(false);
}
insert完结撒花~ 一个插入模块240行代码真的心碎!
删
查
循环遍历查找即可
// 查
bool find(const pair<K, V>& kv) {
Node* current = _root;
while (current != nullptr) {
if (current->_kv.first < kv.first)
current = current->_left;
else if (current->_kv.first > kv.first)
current = current->_right;
else
return true;
}
return false;
}
判断AVL树是否平衡
我们这里通过求子树高度,计算绝对值小于2来实现,因为平衡的本质就是高度差小
bool _ifBalance(Node* root) {
// 通过叶子节点的高度差来实现
if (root == nullptr)
return true;
int leftHeight = _height(root->_left);
int rightHeight = _height(root->_right);
if (rightHeight - leftHeight != root->_bf) {
cout << root->_kv.first << " 平衡因子异常" << endl;
return false;
}
// 求绝对值 以及进入左子树与右子树
return abs(rightHeight - leftHeight) < 2
&& _ifBalance(root->_left)
&& _ifBalance(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 test1() {
AVLTree<string, string> avl_tree;
avl_tree.insert(make_pair("Hello", "2022044026"));
avl_tree.inOrder();
}
// 验证是一个搜索树
void test2() {
int arr[] = {16, 3, 7, 11, 9, 26, 18, 14, 15 };
AVLTree<int, int> avl_tree;
for (auto& e : arr) {
avl_tree.insert(make_pair(e, e));
}
avl_tree.inOrder();
cout << avl_tree.ifBalance() << endl;
}
// 随机数验证实现AVL的平衡
void test3() {
const int N = 30;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
v.push_back(rand());
AVLTree<int, int> tree;
for (auto& e : v) {
tree.insert(make_pair(e, e));
// cout << "insert: " << e << " -> " << tree.ifBalance() << endl;
}
cout << tree.ifBalance() << endl;
tree.inOrder();
}
AVL树的性能
简易AVL树代码
ps:放在.h文件一面使用即可
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
template<class K, class V>
struct AVLTreeNode {
// 三叉链结构
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
AVLTreeNode(const pair<K,V>& kv)
: _left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
// 键值对
pair<K, V> _kv;
// balance fator
int _bf;
};
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* current = _root;
while(current != nullptr) {
parent = current;
if (current->_kv.first > kv.first)
current = current->_left;
else if (current->_kv.first < kv.first)
current = current->_right;
else
return false;
}
current = new Node(kv);
// 判断current在parrent的左还是右
if (parent->_kv.first < kv.first) {
parent->_right = current;
// parent赋值给current的父指针
current->_parent = parent;
}
else {
parent->_left = current;
current->_parent = parent;
}
// 完成插入后开始更新平衡因子_bf
while (parent!=nullptr) { // 能不能current != _root
// 插入后如果在父节点左边
if (current == parent->_left)
parent->_bf--;
else
parent->_bf++;
// 如果父节点为0,跳出循环不再调整
if (parent->_bf == 0) {
break;
}
// 如果父节点不为0,那么往上调整父节点的父节点
else if (parent->_bf == 1 || parent->_bf == -1) {
// 更新current
current = parent;
// 更新parent
parent = parent->_parent;
}
// 为-2 2时需要旋转
else if(parent->_bf == 2 || parent->_bf == -2){
// 单旋转的场景
if (parent->_bf == 2 && current->_bf == 1) {
rotateL(parent);
}
else if (parent->_bf == -2 && current->_bf == -1) {
rotateR(parent);
}
// 双旋转的场景
else if (parent->_bf == 2 && current->_bf == -1) {
rotateRL(parent);
}
else if (parent->_bf == -2 && current->_bf == 1) {
rotateLR(parent);
}
// 只要通过了旋转 就已经实现了平衡因子
break;
}
else {
cout << " 代码有误" << endl;
assert(false);
}
}
return true;
}
// 删
// 删除的难度较大,不过其内部知识根 与 insert相似
// 查
bool find(const K& key) {
Node* current = _root;
while (current != nullptr) {
if (current->_kv.first < key)
current = current->_left;
else if (current->_kv.first > key)
current = current->_right;
else
return true;
}
return false;
}
// 提供外部调用的接口
// 中序遍历 升序打印
void inOrder() { _inOrder(_root); cout << endl; }
// 判断平衡
bool ifBalance() { return _ifBalance(_root); }
// 返回树高度
int Height() { cout << "树的高度为: "; return _height(_root); }
// 返回树节点个数
size_t size() { cout << "节点个数为:"; return _size(_root); }
private:
// 左单旋转
void rotateL(Node* root) {
Node* parent = root;
// 相对根的右边
Node* subR = root->_right;
// 相对根的右边的左边
Node* subRL = subR->_left;
Node* GrandParent = parent->_parent;
// 旋转后的相对位置
subR->_left = parent;
parent->_right = subRL;
if (subRL != nullptr)
subRL->_parent = parent;
// else时为空指针,空指针走->_parent会报错
parent->_parent = subR;
// 将subR设为新的父指针
parent = subR;
// 传入的root一定是 _root 吗
if (GrandParent == nullptr) {
// 是根的话 就需要将parent的位置设为_root
_root = parent;
parent->_parent = GrandParent;
}
else {
// 传入的为子树的根所以grandparent不为空
if (GrandParent->_left == root)
// 不是根的话就需要把grandpa的左右指向parent
GrandParent->_left = parent;
else
GrandParent->_right = parent;
// 实现连接
parent->_parent = GrandParent;
}
parent->_bf = parent->_left->_bf = 0;
}
// 右单旋转
void rotateR(Node* root) {
Node* parent = root;
// 注释参照左旋转
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* GrandParent = parent->_parent;
subL->_right = parent;
parent->_left = subLR;
if (subLR != nullptr)
subLR->_parent = parent;
parent->_parent = subL;
parent = subL;
if (GrandParent == nullptr) {
_root = parent;
parent->_parent = GrandParent;
}
else {
if (GrandParent->_left == root)
GrandParent->_left = parent;
else
GrandParent->_right = parent;
parent->_parent = GrandParent;
}
parent->_bf = parent->_right->_bf = 0;
}
// 双旋 右+左
void rotateRL(Node* root) {
Node* parent = root;
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
// 因为对于parent来说,右树高度过高,需要调整
// 相对root->right 这个节点左树过高需要右旋
rotateR(root->_right);
// 右旋后相对parent来说 右树高度过高需要左旋
rotateL(root);
// 通过 subRL 平衡因子来区分多种情况
if (bf == 0) {
// 插入的节点恰好为subRL
subR->_bf = parent->_bf = subRL->_bf = 0;
}
else if (bf == -1) {
// 往subRL的左树插入
parent->_bf = subRL->_bf = 0;
subR->_bf = 1;
}
else if (bf == 1) {
// 往subRL的右树插入
subRL->_bf = subR->_bf = 0;
parent->_bf = -1;
}
else
assert(false);
}
// 双旋 左+右
void rotateLR(Node* root) {
Node* parent = root;
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
rotateL(root->_left);
rotateR(root);
if (bf == 0) {
subL->_bf = subLR->_bf = parent->_bf = 0;
}
else if (bf == -1) {
parent->_bf = 1;
subL->_bf = subLR->_bf = 0;
}
else if (bf == 1) {
subL->_bf = -1;
parent->_bf = subLR->_bf = 0;
}
else
assert(false);
}
void _inOrder(Node* root) {
if (root == nullptr)
return;
_inOrder(root->_left);
// cout << root->_kv.first << " " << root->_kv.second << endl;
cout << root->_kv.first << " ";
_inOrder(root->_right);
}
bool _ifBalance(Node* root) {
// 通过叶子节点的高度差来实现
if (root == nullptr)
return true;
int leftHeight = _height(root->_left);
int rightHeight = _height(root->_right);
if (rightHeight - leftHeight != root->_bf) {
cout << root->_kv.first << " 平衡因子异常" << endl;
return false;
}
// 求绝对值 以及进入左子树与右子树
return abs(rightHeight - leftHeight) < 2
&& _ifBalance(root->_left)
&& _ifBalance(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;
}
size_t _size(Node* root) {
if (root == nullptr)
return 0;
return _size(root->_left) + _size(root->_right) + 1;
}
private:
// 一定需要初始化 不然代码运行有bug
Node* _root = nullptr;
};
// 测试区
void test1() {
AVLTree<string, string> avl_tree;
avl_tree.insert(make_pair("Hello", "2022044026"));
avl_tree.inOrder();
}
// 验证是一个搜索树
void test2() {
int arr[] = {16, 3, 7, 11, 9, 26, 18, 14, 15 };
AVLTree<int, int> avl_tree;
for (auto& e : arr) {
avl_tree.insert(make_pair(e, e));
}
avl_tree.inOrder();
cout << avl_tree.ifBalance() << endl;
}
// 随机数验证实现AVL的平衡
void test3() {
const int N = 30000;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
v.push_back(rand()+i);
AVLTree<int, int> tree;
for (auto& e : v) {
tree.insert(make_pair(e, e));
// cout << "insert: " << e << " -> " << tree.ifBalance() << endl;
}
cout << tree.ifBalance() << endl;
// tree.inOrder();
cout << tree.Height() << endl;
cout << tree.size() << endl;
cout << tree.find(10) << endl;
}