前言:
为什么要有AVL树的概念?AVL树是为了解决什么问题?我们已经知道有搜索二叉树,可以通过比较的方式快速查找元素,但是它有一个致命的弱点------一旦选取靠近边界的元素,那么树的结构的高度就会过高,查找的效率就会降低至log(N),这就失去了其本身的意义,那么有什么方法可以使其的两边的高度平衡呢?AVL树就是为了解决这个问题的其中一种方法。

一. AVL的概念
AVL树实现这里我们引入⼀个平衡因子(balance factor)的概念,每个结点都有⼀个平衡因子,任何结点的平衡因子等于右子树的高度减去左子树的高度,也就是说任何结点的平衡因子等于0/1/-1,AVL树并不是必须要平衡因子,但是有了平衡因子可以更方便我们去进行观察和控制树是否平衡,就像⼀个风向标⼀样。
特性:
AVL树是最先发明的自平衡⼆叉查找树,AVL可以是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的左右子树都是AVL树,且左右子树的高度差的绝对值不超过1。

二.AVL树的模拟实现
1.单节点
特点:
1.使用struct,方便使用成员变量
2.使用pair来包含:用于比较的_key,用于存储的映射值
3.三指针(联系):_parent指向上一个节点,_left指向左节点,_right指向右节点
4.因子:_bf
cpp
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode(const pair<K,V>&kv)
:_kv(kv)
, _parent(nullptr)
, _left(nullptr)
, _right(nullptr)
,_bf(0)
{}
pair<K, V>_kv;
AVLTreeNode<K, V>* _parent;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
int _bf;
};
2.类结构
使用指针_root来管理
3.功能的实现
3.1插入
首先插入操作是基于搜索二叉树的结构,用比较来查找适合插入的位置(这个就不详细讲解了),
然后插入元素可能破坏二叉树的平衡,这时候就需要去调整树的结构。接下来详细讲解步骤。
发现AVL二叉树失去平衡:通过平衡因子的大小,我们是可以了解树是否平衡,但是由于插入新元素,旧的因子就需要更新:
步骤:
1.由插入的节点处开始进行更新因子,刚插入的节点为0,通过节点parent指针走到上一个节点,判断新节点是插入在该节点的左边还是右边,左边就对该节点的平衡因子- -,右边就++,观察平衡因子是否变为2/-2或者变为0,如果都不是就继续往上循环步骤(判断,因子的加减)。
2.发现因子变成0,退出循环。为什么?因为以该0处节点往上看(把该处当作一个左子树或者右子树),新元素的插入1没有影响整体的平衡度,从该节点往下看(把该节点当根),新元素的插入只是将自身变得更加平衡了。
如图

2.发现平衡因子为2/-2需要进行结构调整,但是有以下四种情况
(1)需要进行右旋转
(2)需要进行左旋转
(3)需要进行左右双旋转
(4)需要进行右左双旋转
调整的部分后面再进行详细讲解,我们先来搭一下查找的框架(我们实现的是不允许有相同元素的)
cpp
bool insert(const pair<K, V>&kv)
{
Node* newnode = new Node(kv);
if (_root == nullptr)
{
_root = newnode;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
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;
}
}
//插入
if (parent->_kv.first < kv.first)
{
parent->_right = newnode;
}
else
{
parent->_left = newnode;
}
newnode->_parent = parent;
//更新并观察平衡因子
cur = newnode;
while (parent)
{
//更新平衡因子
if (parent->_left == cur)
{
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)
{
//......
break;
}
else
{
assert(false);
}
}
return true;
}
3.2结构调整
(1)右旋转
首先介绍一下需要右旋转的情况:

需要右旋转的情况都可以抽象成图中的第一阶段,没有例外,所以我们仅通过左子树头节点的-1就可以判断需要右旋转(-2并不能判断是不是需要右旋转)
对各个节点的指针的指向进行操作就可以了
操作的注意点:
1.subLR可能是空,需要判断(防止链接parent时解引用空指针)
2.当旋转平衡之后,需要更新parent和SubL 的平衡因子,AVL旋转后平衡则SubL的平衡因子为0,且parent的平衡因子也为0'
3.如果parent为_root节点,需要更新_root
cpp
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)//判断是否为空
{
//分辨是左子树还是右子树
if (parentparent->_left == parent)
{
parentparent->_left = subL;
}
else
{
parentparent->_right = subL;
}
}
else//parentparent为空表明parent是_root
{
_root = subL;//更新_root
}
subL->_parent = parentparent;
//更新因子
subL->_bf = parent->_bf = 0;
}
(2)左旋转

需要左旋转的情况都可以抽象成图中的第一阶段,没有例外,所以我们仅通过右子树头节点的1就可以判断需要左旋转(2并不能判断是不是需要左旋转)
具体操作和右旋转完全类似
cpp
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)
{
if (parentparent->_left == parent)
{
parentparent->_left = subR;
}
else
{
parentparent->_right = subR;
}
}
else
{
_root = subR;
}
subR->_parent = parentparent;
subR->_bf = parent->_bf = 0;
}
(3)左右双旋转
我们先来观察一下需要这种操作的情况:
情况1:

新节点插入在9的左边,首先以6节点为parent开始左旋转,然后就可以以10节点为parent开始右旋转,达到平衡
情况2:

新节点插入在8的右边,首先以6节点为parent开始左旋转,然后就可以以10节点为parent开始右旋转,达到平衡
情况3:

新节点插入在6的右边,首先以6节点为parent开始左旋转,然后就可以以10节点为parent开始右旋转,达到平衡
根据上面的三种情况,我们可以总结出一个规律:新节点都是插入在subL的右子树(subLR),我们可以根据subL是否为1来判断是否需要左右旋转
所以我们可以将所有情况抽象成一幅图:

操作步骤:
1.以subL为parent进行左旋转
2.以原parent来进行右旋转
3.更新各处因子:原parent可能为1或者0,subL可能为0或者-1(根据插入到subLR的左子树还是右子树),subLR最终都为0。注意:要在旋转前拿到subLR的因子才能判断
cpp
void RotateLR(Node*parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;//要先拿
RotateL(parent->_left);
RotateR(parent);//subLR的因子已经更新好了
//更新因子
if (bf == 0)//subLR两边都有
{
subL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subL->_bf = -1;
parent->_bf = 0;
}
else
{
subL->_bf = 0;
parent->_bf = 1;
}
}
(4)右左双旋转
右左旋转和左右旋转完全类似。也可以分为三种情况(就不过多详细解释了),我们直接来看总结:新节点都是插入在subL的右子树(subRL),我们可以根据subR是否为1来判断是否需要右左旋转
所以我们可以将所有情况抽象成一幅图:

操作步骤:
1.以subR为parent进行右旋转
2.以原parent来进行左旋转
3.更新各处因子:原parent可能为-1或者0,subL可能为0或者1(根据插入到subRL的左子树还是右子树),subRL最终都为0。注意:要在旋转前拿到subRL的因子才能判断
cpp
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;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else
{
subR->_bf = 1;
parent->_bf = 0;
}
}
我们来看具体insert的代码:
cpp
bool insert(const pair<K, V>&kv)
{
Node* newnode = new Node(kv);
if (_root == nullptr)
{
_root = newnode;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
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;
}
}
//插入
if (parent->_kv.first < kv.first)
{
parent->_right = newnode;
}
else
{
parent->_left = newnode;
}
newnode->_parent = parent;
//平衡因子
cur = newnode;
while (parent)
{
if (parent->_left == cur)
{
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->_left!=nullptr&&parent->_left->_bf == -1)
{
RotateR(parent);
break;
}
if (parent->_left != nullptr&&parent->_left->_bf == 1)
{
RotateLR(parent);
break;
}
if (parent->_right != nullptr&&parent->_right->_bf == 1)
{
RotateL(parent);
break;
}
if (parent->_right != nullptr&&parent->_right->_bf == -1)
{
RotateRL(parent);
break;
}
}
else
{
assert(false);
}
}
return true;
}
3.3查找------find
cpp
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;
}
3.4判断是否为AVL树
运用:子函数+高度遍历
cpp
int Hight(Node* root)
{
if (root == nullptr)
{
return 0;
}
int righthight = Hight(root->_right);
int lefthight = Hight(root->_left);
return righthight > lefthight ? righthight + 1 : lefthight + 1;
}
bool Is_AVLTree(Node* root)
{
if (root == nullptr)
{
return true;
}
int righthight = Hight(root->_right);
int lefthight = Hight(root->_left);
int diff = righthight - lefthight;
if (diff <= -2 || diff >= 2)
{
cout << "平衡失常" << endl;
return false;
}
if (root->_bf != diff)
{
cout << "平衡因子失常" << endl;
return false;
}
return Is_AVLTree(root->_left) && Is_AVLTree(root->_right);
}
bool test_Is_AVLTree()
{
return Is_AVLTree(_root);
}
3.5前序打印
cpp
void AVLPREorder()
{
PREorder(_root);
}
void PREorder(Node* root)
{
if (root == nullptr)
{
return;
}
cout << root->_kv.first << " ";
//cout << endl;
PREorder(root->_left);
PREorder(root->_right);
}
完整代码:
头文件:AVL.h
cpp
#pragma once
#include<iostream>
#include<utility>
#include<assert.h>
#include<vector>
using namespace std;
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode(const pair<K,V>&kv)
:_kv(kv)
, _parent(nullptr)
, _left(nullptr)
, _right(nullptr)
,_bf(0)
{}
pair<K, V>_kv;
AVLTreeNode<K, V>* _parent;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
int _bf;
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree() = default;
bool insert(const pair<K, V>&kv)
{
Node* newnode = new Node(kv);
if (_root == nullptr)
{
_root = newnode;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
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;
}
}
//插入
if (parent->_kv.first < kv.first)
{
parent->_right = newnode;
}
else
{
parent->_left = newnode;
}
newnode->_parent = parent;
//平衡因子
cur = newnode;
while (parent)
{
if (parent->_left == cur)
{
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->_left!=nullptr&&parent->_left->_bf == -1)
{
RotateR(parent);
break;
}
if (parent->_left != nullptr&&parent->_left->_bf == 1)
{
RotateLR(parent);
break;
}
if (parent->_right != nullptr&&parent->_right->_bf == 1)
{
RotateL(parent);
break;
}
if (parent->_right != nullptr&&parent->_right->_bf == -1)
{
RotateRL(parent);
break;
}
}
else
{
assert(false);
}
}
return true;
}
//右旋
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)
{
if (parentparent->_left == parent)
{
parentparent->_left = subL;
}
else
{
parentparent->_right = subL;
}
}
else
{
_root = subL;
}
subL->_parent = parentparent;
//更新因子
subL->_bf = parent->_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)
{
if (parentparent->_left == parent)
{
parentparent->_left = subR;
}
else
{
parentparent->_right = subR;
}
}
else
{
_root = subR;
}
subR->_parent = parentparent;
subR->_bf = parent->_bf = 0;
}
//左右旋
void RotateLR(Node*parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);//subLR的因子已经更新好了
//更新因子
if (bf == 0)//subLR两边都有
{
subL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subL->_bf = -1;
parent->_bf = 0;
}
else
{
subL->_bf = 0;
parent->_bf = 1;
}
}
//右左旋
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;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else
{
subR->_bf = 1;
parent->_bf = 0;
}
}
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 Hight(Node* root)
{
if (root == nullptr)
{
return 0;
}
int righthight = Hight(root->_right);
int lefthight = Hight(root->_left);
return righthight > lefthight ? righthight + 1 : lefthight + 1;
}
bool Is_AVLTree(Node* root)
{
if (root == nullptr)
{
return true;
}
int righthight = Hight(root->_right);
int lefthight = Hight(root->_left);
int diff = righthight - lefthight;
if (diff <= -2 || diff >= 2)
{
cout << "平衡失常" << endl;
return false;
}
if (root->_bf != diff)
{
cout << "平衡因子失常" << endl;
return false;
}
return Is_AVLTree(root->_left) && Is_AVLTree(root->_right);
}
bool test_Is_AVLTree()
{
return Is_AVLTree(_root);
}
void AVLPREorder()
{
PREorder(_root);
}
void PREorder(Node* root)
{
if (root == nullptr)
{
return;
}
cout << root->_kv.first << " ";
//cout << endl;
PREorder(root->_left);
PREorder(root->_right);
}
private:
Node* _root = nullptr;
};
测试文件:test.cpp
cpp
void test01()
{
vector<int> R({ 10,12,7,5,8,4,3 });
vector<int> L({ 10,15,6,20,14,25 });
vector<int> vv({ 16, 3, 7, 11, 9, 26, 18, 14, 15 });
AVLTree<int, int> tree;
for (auto e : vv)
{
tree.insert({e,e});
}
tree.AVLPREorder();
if (tree.test_Is_AVLTree())
{
cout << "平衡" << endl;
}
}