1.概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年发明了一种解决上述问题的方法:
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1),如下图:

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在logn,搜索时间复杂度O(logN)
2.AVL树的插入
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。
平衡因子(_bf)就是该节点右子树高度减去左子树高度
那么 AVL树的插入过程可以分为两步:
1. 按照二叉搜索树的方式插入新节点
2. 调整节点的平衡因子
调整节点的平衡因子
一个合格的AVL树在插入之前,就应该满足AVL树的条件,插入节点的父亲节点的平衡因子只有三种可能(-1,0,1)

插入分以下几种情况:

第一种和第四种插入后子树的高度没有变化,所以上面的节点的平衡因子都不会变,平衡因子的更新可以结束了
第二种和第三种插入后子树的高度发生变化,所以上面的节点的平衡因子要变,要继续更新平衡因子
继续更新后如果有一个节点的平衡因子不再是(-1,0,1)也就是变成2或者-2,就要进行处理
平衡因子变成2或者-2的节点的子树,对于这个子树,一定是高的那个子树插入一个节点,导致在原先就高的那一边的子树,更加高了
我们可以分成以下几种情况继续分析
2 1树(左旋)

平衡因子不合格的本质是树的高度差太大,所以需要降高度。
对8左旋:

-2 -1树(右旋)

对8右旋:

-2 1树(左右旋)

先对8左旋再对10右旋:

这里加在b,也可以加在c,还有可能9就是新增节点,但是中间旋转的过程都是不变的,改变的只有平衡因子,但顶部的平衡因子一定为0。
2 -1 树(右左旋)

先对10右旋再对8左旋:

和上面一样,这里加在c,也可以加在b,还有可能9就是新增节点,但是中间旋转的过程都是不变的,改变的只有平衡因子,但顶部的平衡因子一定为0。
3.AVL树的删除
删除我熬了一个通宵搞明白的,不容易啊
3.1.删除节点
先考虑一下,删除有哪些情况:
1.删除的节点左右都为空,删除的就是叶子节点
2.删除的节点左右一边为空,只有以下两种情况,因为这是AVL树,所有节点的平衡因子只能是(-1,0,1)
现在已知一边为空,所以平衡因子=另一边子树的高度,所以只能有一个节点

3.删除的节点的左右都都不为空,这种情况,和搜索二叉树的找月嫂思想是一样的
这三种情况的解决方式有异曲同工之处
情况1:删除叶子节点,如果他是父亲的左孩子,(bf)平衡因子++,反之--
情况2:可以把被删除的节点存储的值和他的唯一的孩子交换,然后删除他的孩子,问题转换成情况1
情况3:找到左子树的最大值节点,与其交换一下存储的内容,问题转换成删除那个左子树最大的节点,这个节点,又符合左为空的情况,如果右也为空就是情况1,反之情况2
删除节点最后都会转为删除叶子节点
3.2.平衡因子更新
节点删除了,被删除节点的父亲节点的平衡因子也更新了,但是是否需要继续更新,当前位置是否平衡都需要考虑
平衡因子的正常范围是[-1,1]
被删除节点的父亲节点平衡因子可以分为以下三种情况:
1.bf == 0

说明原先该节点的bf == -1 或者 1 变成 0 后 说明原先高的那一边被删除了,导致高的子树高度降低,变得和另一边一样高,整颗树树的高度发生了变化,需要继续更新
2.bf == 1 或者 -1

说明原先该节点的bf == 0 0 变成 1 后 说明原先平衡,删除了一边,导致一边子树的高度变低了,但整颗树的高度没有变化,不需要更新
3. bf == 2 或者 bf == -2
这颗子树不平衡了,需要处理
3.3.旋转
不平衡分成两种大情况:
1.由于删除节点,导致当前子树不平衡

2.由于删除节点后导致平衡因子更新,而导致树不平衡
在更新平衡因子过程中,cur的bf一定是变成0,才会进一步,导致parent变化
在这个过程中,parent的bf变成2或者-2,是判断是否需要旋转的条件
而具体该怎么旋转,就由叔叔决定,因为cur的bf一定是0。和上面插入的旋转道理是一样的,也是这四种旋转。
这里右六种情况:
因为parent的bf可以是-2或者2,uncle的bf可以是-1,0,1
| parent->bf | uncle->bf | 旋转方式 |
| 2 | 1 | 左旋 |
| 2 | 0 | 左旋 |
| 2 | -1 | 右左双旋 |
| -2 | 1 | 右旋 |
| -2 | 0 | 右旋 |
| -2 | -1 | 左右双旋 |
在旋转之后还需要更新cur和parent,因为还有继续更新平衡因子的需要
4.源代码
#pragma once
#include<assert.h>
template<class K, class V>
struct AVLtreeNode
{
AVLtreeNode* _left;
AVLtreeNode* _right;
AVLtreeNode* _parent;
pair<K, V> _kv;
int _bf;
AVLtreeNode(const K& key, const V& val)
:_left(nullptr),
_right(nullptr),
_parent(nullptr),
_kv(key, val),
_bf(0)
{}
};
template<class K, class V>
class AVLtree
{
public:
typedef AVLtreeNode<K, V> Node;
typedef pair<K, V> value_type;
AVLtree() :_root(nullptr){}
bool insert(const value_type& val)
{
if (_root == nullptr)
{
_root = new Node(val.first, val.second);
return true;
}
// 找插入位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
parent = cur;
if (cur->_kv.first > val.first)
cur = cur->_left;
else if (cur->_kv.first < val.first)
cur = cur->_right;
else
return false;
}
// 插入
if (parent->_kv.first > val.first)
{
parent->_left = new Node(val.first, val.second);
cur = parent->_left;
cur->_parent = parent;
}
else
{
parent->_right = new Node(val.first, val.second);
cur = parent->_right;
cur->_parent = parent;
}
// 更新_bf平衡因子
while (parent)
{
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;
if (parent->_bf == -1 || parent->_bf == 1)
{
//继续更新
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 0)
{
//无需继续更新
break;
}
else if (parent->_bf == -2 || parent->_bf == 2)
{
//不平衡,要旋转
if (parent->_bf > 0 && cur->_bf > 0)
RotateL(parent);
else if (parent->_bf < 0 && cur->_bf < 0)
RotateR(parent);
else if (parent->_bf > 0 && cur->_bf < 0)
RotateRL(parent);
else
RotateLR(parent);
break;
}
else
{
assert(false);
}
}
return true;
}
bool erase(const K& key)
{
if (_root == nullptr)
{
return false;
}
//找key
Node* cur = _root;
while(cur)
{
if (cur->_kv.first > key)
{
cur = cur->_left;
}
else if (cur->_kv.first < key)
{
cur = cur->_right;
}
else//找到了
{
if (cur->_left == nullptr && cur->_right == nullptr)//删除的节点是叶子节点
{
cur = DeleteNode(cur);
}
else if (cur->_left == nullptr)
{
Node* del = cur->_right;
cur->_kv = del->_kv;
cur = DeleteNode(del);
}
else if (cur->_right == nullptr)
{
Node* del = cur->_left;
cur->_kv = del->_kv;
cur = DeleteNode(del);
}
else
{
//找左子树的最大节点
Node* leftmax = cur->_left;
while (leftmax->_right)
leftmax = leftmax->_right;
cur->_kv = leftmax->_kv;
//是否需要托孤
if(leftmax->_left)
{
Node* del = leftmax->_left;
leftmax->_kv = del->_kv;
cur = DeleteNode(del);
}
else
{
Node* del = leftmax;
cur = DeleteNode(del);
}
}
if (cur == nullptr)//根节点被删除
return true;
if (cur->_bf == 2 || cur->_bf == -2)//删除那个节点导致不平衡
{
Node* parent = cur;
cur = nullptr;
Rotate_delete_check(parent, cur);
}
//更新平衡因子
Node* parent = cur->_parent;
while (parent && cur->_bf == 0)//cur的平衡因子是0才需要更新
{
if (parent->_left == cur)
parent->_bf++;
else
parent->_bf--;
if (parent->_bf == -1 || parent->_bf == 1)
{
//无需继续更新
break;
}
else if (parent->_bf == 0)
{
//继续更新
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == -2 || parent->_bf == 2)
{
cout << "//////////////////////////////" << endl;
Rotate_delete_check(parent, cur);
}
}
return true;
}
}
return false;
}
bool check_balance()
{
return _check_balance(_root);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
Node* DeleteNode(Node* cur)//删除叶子节点,并且返回其父亲
{
Node* del = cur;
Node* parent = cur->_parent;
if (parent == nullptr)//cur是根节点
{
_root = nullptr;
delete del;
return nullptr;
}
if (parent->_left == del)
{
parent->_left = nullptr;
parent->_bf++;
}
else
{
parent->_right = nullptr;
parent->_bf--;
}
delete del;
return parent;
}
void Rotate_delete_check(Node*& parent, Node*& cur)
{
Node* uncle;
if (parent->_left == cur)
uncle = parent->_right;
else
uncle = parent->_left;
if (parent->_bf == 2 && uncle->_bf == 1)
{
RotateL(parent);
cur = uncle;
}
else if (parent->_bf == -2 && uncle->_bf == -1)
{
RotateR(parent);
cur = uncle;
}
else if (parent->_bf == 2 && uncle->_bf == -1)
{
cur = uncle->_left;
RotateRL(parent);
}
else if (parent->_bf == -2 && uncle->_bf == 1)
{
cur = uncle->_right;
RotateLR(parent);
}
else if (parent->_bf == 2 && uncle->_bf == 0)
{
RotateL(parent);
parent->_bf = 1;
uncle->_bf = -1;
cur = uncle;
}
else if (parent->_bf == -2 && uncle->_bf == 0)
{
RotateR(parent);
parent->_bf = -1;
uncle->_bf = 1;
cur = uncle;
}
else assert(false);
parent = cur->_parent;
}
void balence_check(Node* parent, Node* cur)
{
if (parent == nullptr)
return;
if (parent->_bf == 0)
{
//无需继续更新
}
else if (parent->_bf == -2 || parent->_bf == 2)
{
//不平衡,要旋转
if (parent->_bf > 0 && cur->_bf > 0)
RotateL(parent);
else if (parent->_bf < 0 && cur->_bf < 0)
RotateR(parent);
else if (parent->_bf > 0 && cur->_bf < 0)
RotateRL(parent);
else
RotateLR(parent);
}
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
bool _check_balance(Node* root)
{
if (root == nullptr)
return true;
int lefthight = TreeHight(root->_left);
int righthight = TreeHight(root->_right);
if (abs(lefthight - righthight) >= 2 || righthight - lefthight != root->_bf)
{
cout << root->_kv.first << ":" << root->_bf << endl;
return false;
}
return _check_balance(root->_left) && _check_balance(root->_right);
}
int TreeHight(Node* root)
{
if (root == nullptr)
return 0;
int lefthight = TreeHight(root->_left);
int righthight = TreeHight(root->_right);
return lefthight > righthight ? lefthight + 1: righthight + 1;
}
void RotateL(Node* parent)//左单旋
{
Node* Rightson = parent->_right;
//旋转节点之间的链接
parent->_right = Rightson->_left;
if (parent->_right)
parent->_right->_parent = parent;
Rightson->_left = parent;
Node* pparent = parent->_parent;
parent->_parent = Rightson;
//更新祖先的链接
if (pparent == nullptr)
{
Rightson->_parent = nullptr;
_root = Rightson;
}
else
{
if (pparent->_kv.first > Rightson->_kv.first)
pparent->_left = Rightson;
else
pparent->_right = Rightson;
Rightson->_parent = pparent;
}
parent->_bf = Rightson->_bf = 0;
}
void RotateR(Node* parent)//右单旋
{
Node* Leftson = parent->_left;
//旋转节点之间的链接
parent->_left = Leftson->_right;
if (parent->_left)
parent->_left->_parent = parent;
Leftson->_right = parent;
Node* pparent = parent->_parent;
parent->_parent = Leftson;
//更新祖先的链接
if (pparent == nullptr)
{
Leftson->_parent = nullptr;
_root = Leftson;
}
else
{
if (pparent->_kv.first > Leftson->_kv.first)
pparent->_left = Leftson;
else
pparent->_right = Leftson;
Leftson->_parent = pparent;
}
parent->_bf = Leftson->_bf = 0;
}
void RotateLR(Node* parent)
{
Node* Leftson = parent->_left;
Node* LRightsson = Leftson->_right;
int bf = LRightsson->_bf;
RotateL(Leftson);
RotateR(parent);
//平衡因子更新
if (bf == 1)
{
Leftson->_bf = -1;
}
else if (bf == -1)
{
parent->_bf = 1;
}
else if (bf == 0) {}
else
{
assert(false);
}
}
void RotateRL(Node* parent)
{
Node* Rightson = parent->_right;
Node* RLeftsson = Rightson->_left;
int bf = RLeftsson->_bf;
RotateR(Rightson);
RotateL(parent);
//平衡因子更新
if (bf == 1)
{
parent->_bf = -1;
}
else if (bf == -1)
{
Rightson->_bf = 1;
}
else if(bf == 0){}
else
{
assert(false);
}
}
private:
Node* _root;
};
写完了。

本文详细阐述了AVL树的概念,强调了其通过维持节点左右子树高度差不超过1来保持高效搜索。文章介绍了AVL树的插入过程,包括遵循二叉搜索树规则插入新节点并调整平衡因子,以及删除节点后的平衡因子更新和可能的旋转操作。
1325

被折叠的 条评论
为什么被折叠?



