目录
平衡因子
AVL树的概念
节点结构的定义
AVL树函数声明
AVL树的插入
AVL树的验证
前面的博客介绍了搜索二叉树,也提到了搜索二叉树的效率问题,当插入数据有序或者接近有序时,搜索二叉树就会退化成单支树,查找时间复杂度就变成O(N)了,因此大佬们在搜索二叉树的基础上做了一些改进,使得查找效率保持在O(logN)。本篇博客的内容也是搜索二叉树的进阶,如果对搜索二叉树还有疑问的朋友,建议先看看前面的博客 搜索二叉树-****博客
平衡因子
上面说到,单纯的搜索二叉树有可能出现左右子树高度差过大导致查找效率低下,而AVL树引入了一个新的概念,就是平衡因子,每颗树的平衡因子就是右子树高度-左子树高度,可能为正/负/零
AVL树的概念
一棵 AVL 树或者是空树,或者是具有以下性质的二叉搜索树:1.它的左右子树都是 AVL 树2左右子树高度之差 ( 简称平衡因子 ) 的绝对值不超过 1(-1/0/1)
节点结构的定义
注意:我们下面实现的AVL树,实现的是kv模型,也就是存储key值和对应的value值, 模拟map
template <class K, class V>
struct AVLTreeNode
{
//三叉链
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent; //方便进行AVL树进行调平
pair<K, V> _kv; //存储键值对
int _bf; //平衡因子(balance factor)
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
,_bf(0)
{}
};
AVL树函数声明
template <class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
void RotateL(Node* parent); //左单旋
void RotateR(Node* parent); //右单旋
void RotateLR(Node* parent); //左右双旋
void RotateRL(Node* parent); //右左双旋
bool insert(const pair<K, V>& kv); //插入
void InOrder(); //中序遍历
int Height(); //求二叉树高度
bool IsBalance();//判断是否是AVL树
private:
void _InOrder(Node* root);
int _Height(Node* root);
bool _IsBalance(Node* root);
private:
Node* _root;
};
AVL树的插入
插入的主逻辑和搜索二叉树基本一样,不一样的就是平衡因子的变化 以及 如果插入后不满足AVL树平衡的特点,需要调平衡。
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;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
//剩余代码---平衡因子的改变以及AVL树调平衡
return true;
}
插入后当前子树平衡因子的变化
1.插入到树的左边,平衡因子 --
2.插入到树的右边,平衡因子++
注意:
1.插入数据之后,只会影响从整棵树的根到插入节点的这条路径上的节点(插入节点的祖先)的平衡因子(也只是可能会影响)
插入节点后对祖先的平衡因子(bf)影响
判断插入新的节点之后祖先的平衡因子是否变化,关键点就是看当前子树的高度是否变化,如果高变化了,就会向上继续影响祖先,高度不变,就不会影响祖先
注意:下面的parent是相对的,需要向上更新时,parent就会变化
1.parent的bf更新后是0,说明更新之前是-1/1, 更新之前一边高,一边低,而新插入节点填在了低的一边,所以更新后当前子树的高度不变,不用往上继续更新
2.parent的bf更新后是1/-1,说明更新之前是0, 也就时间更新之前左右子树一样高,插入节点之后一边高一边低,当前子树的高度发生变化,就影响了祖先,要继续往上更新, 更新到什么时候为止呢???
①更新到parent的平衡因子是0了,就停下来!
②更新到整棵树的根了,就停下了!
3.parent的bf更新是2/-2,说明更新之后AVL树失衡了, 需要调平衡
调平衡的方法:
1.左单旋
2.右单旋
3.右左双旋
4.左右双旋
左单旋 --- 新节点插入较高右子树的右侧--- cur->_bf == 1 && parent->bf == 2
具体图:
抽象图:
左单旋代码
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
subR->_left = parent;
Node* parentParent = parent->_parent;
parent->_parent = subR;
if (subRL)
subRL->_parent = parent;
if (_root == parent)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
parent->_bf = subR->_bf = 0;
}
右单旋 --- 新节点插入较高左子树的左侧 --- cur->_bf == -1 && parent->_bf == -2
右单旋代码
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 (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
subL->_bf = parent->_bf = 0;
}
右左双旋 --- 新节点插入较高右子树的左侧 --- parent->_bf == 2 && cur->_bf == -1
注:为了方便演示,我们将上述讲解右单旋/左单旋抽象图中的b树进行拆分
右左双旋三种情况:
①新节点插入到b
subRL平衡因子更新成-1
②新节点插入到c
subRL平衡因子更新成1
③60自己就是新增
subRL平衡因子更新成0
右左双旋代码
从上面三张图可以看到,可以根据插入新节点之后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)
{
// subRL自己就是新增
parent->_bf = subR->_bf = subRL->_bf = 0;
}
else if (bf == -1)
{
// subRL的左子树新增
parent->_bf = 0;
subRL->_bf = 0;
subR->_bf = 1;
}
else if (bf == 1)
{
// subRL的右子树新增
parent->_bf = -1;
subRL->_bf = 0;
subR->_bf = 0;
}
else
{
assert(false);
}
}
左右双旋 --- 新节点插入较高左子树的右侧 --- parent->_bf == -2 && cur->_bf == 1
左右双旋三种情况:
①新节点插入到b
②新节点插入到c
③60自己就是新增
左右双旋代码
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf; //subLR不可能为nullptr,因为subL的平衡因子是1
//1、以subL为旋转点进行左单旋
RotateL(subL);
//2、以parent为旋转点进行右单旋
RotateR(parent);
//3、更新平衡因子
if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 0)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false); //在旋转前树的平衡因子就有问题
}
}
AVL树插入代码
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;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_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)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
AVL树的验证
中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
//中序遍历
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
求二叉树高度
int Height()
{
return _Height(_root);
}
private:
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;
}
判断是否是AVL树
bool IsBalance()
{
return _IsBalance(_root);
}
private:
bool _IsBalance(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
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
测试AVL树
#include <iostream>
#include <assert.h>
using namespace std;
#include "AVL.h"
void test1()
{
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
AVLTree<int, int> t;
for (auto e : a)
{
t.Insert(make_pair(e, e));
}
t.InOrder();
cout << t.IsBalance() << endl;
}
void test2()
{
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTree<int, int> t;
for (auto e : a)
{
t.Insert(make_pair(e, e));
}
t.InOrder();
cout << t.IsBalance() << endl;
}
#include <vector>
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());
cout << v.back() << endl;
}
AVLTree<int, int> t;
for (auto e : v)
{
t.Insert(make_pair(e, e));
cout << "Insert:" << e << "->" << t.IsBalance() << endl;
}
cout << t.IsBalance() << endl;
}
int main()
{
//test1();
//test2();
test3();
return 0;
}