文章目录
一、AVL树的基本概念
二叉搜索树能够实现高效查找O(logN)。但是遇到特殊情况,比如升序或者降序就会退化为单支树,此时查找的效率就退化到了O(N)。
AVL树又称平衡搜索树,它保证了当向搜索树中插入新的节点后,如果能使每个节点的左右子树高度只差绝对值不超过1,即保证了这棵树是平衡的,即有如下特点:
- 左右子树都是平衡的(高度差的绝对值不超过1)
- 如果一棵二叉搜索树的高度是平衡的,它就是AVL树。它的查找效率为logn
1.1. 平衡因子
为了实现搜索树的平衡,引入了平衡因子的概念(当然也有别的方法实现平衡,比如树的高度,使用平衡因子会更方便一些)。每个节点都有一个平衡因子,它是该节点右子树高度与左子树高度的差(也可以是左子树与右子树的高度差,区别不大),如果平衡因子为0,-1或1,则说明该节点的左右子树平衡,如果为2,-2则说明不平衡,此时我们需要对该节点的左右子树进行旋转操作。
1.2. 平衡因子的调节
以右子树高度减左子树高度的平衡因子为例:
-
新增的节点在父节点的右侧时
parent->bf++
; -
新增的节点再父节点的左侧时
parent->bf--
; -
更新平衡因子之后、如果
parent->bf==0
说明以当前节点为根的子树是平衡的,即不会再对上一层的父节点的平衡产生影响.
-
当节点的平衡因子为1或者-1的时候,说明新增的节点对父节点的平衡产生了影响(因为原来该节点的平衡因子为0,左右子树高度相等,变为1或-1说明该节点的左子树或右子树的高度变了,以该节点为根的树的高度变了),此时需要向上继续调节平衡因子,直到平衡因子为0或到根节点为止。
-
当节点的平衡因子为2或者-2的时候,说明此时以该节点为根的树,已经失去了平衡,此时需要进行旋转处理
二、三叉链的AVL树
2.1. 旋转的四种情况
右单旋
右单旋的判断条件:
parent->bf=-2
表示左子树高cur->bf=-1
表示新插入的节点在当前节点的左侧
处理方式:
- 此时左边高、右边低,因此需要使右边的高度增加,左边的高度降低
- b树位于30的右侧,因此按照搜索树树的性质,是可以转移到60的左侧的
- 将b树转移之后,再将60链接在30的右侧,此时30左右两侧的高度都是h+1,即平衡了
注意:
在旋转时,parent的父节点pparent也需要进行修改,让其指向cur,当根节点就是parent时,此时让cur作根节点。
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curR = cur->_right;//cur的右子树
Node* pparent = parent->_parent;//保存parent的父亲节点
//将cur右子树链接到parent的左侧
parent->_left = curR;
if (curR)
curR->_parent = parent;
//将parent连接到cur的右侧
cur->_right = parent;
parent->_parent = cur;
//将cur与pparent链接起来
if (pparent == nullptr)//cur变成新的根
{
_root = cur;
cur->_parent = nullptr;
}
else//pparent不为根
{
cur->_parent = pparent;
if (parent == pparent->_left)//在上一级节点的左侧
{
pparent->_left = cur;
}
else
{
pparent->_right = cur;
}
}
//平衡因子的更新
parent->_bf = 0;
cur->_bf = 0;
}
左单旋
左单旋和右单旋差不多:
左单旋的判断条件:
parent->bf==2
说明右子树高了cur->vf==1
说明在当前节点的右侧增加了一个新的节点(1只能是由0变来的)
处理方式:
- 此时左低右高,因此需要将左边提高,右边降低
- b位于30的右侧,60的左侧,挪动之后不会影响BS的性质
- 将30往下压,60往上提升之后得到以60位根的树的左右两侧的高度都是h+1
void RotateL(Node* parent)//左旋
{
Node* cur = parent->_right;//右变高,不可能为空
Node* curL = cur->_left;
Node* pparent = parent->_parent;
//cur左子树作为parent右子树
parent->_right = curL;
if (curL)
curL->_parent = parent;
//parent作为cur左子树
cur->_left = parent;
parent->_parent = cur;
//cur链接到pprent上
if (pparent == nullptr)//根
{
_root = cur;
cur->_parent = nullptr;
}
else//不为根
{
cur->_parent = pparent;
//判断链接在哪一侧
if (pparent->_left == parent)
{
pparent->_left = cur;
}
else
{
pparent->_right = cur;
}
}
//平衡因子的更新
parent->_bf = 0;
cur->_bf = 0;
}
左右双旋
此时无论是左旋、或者右旋都无法使树平衡,因为左旋和右旋只适用于平衡因子变化是直线的情况,这里平衡因子是折线,此时可以先对30进行左旋,然后对90进行右旋:
对于在c插入的情况也是一样,只不过最终的平衡因子不一样:
判断条件:
- 当
parent -> bf=-2
时,此时表示左高右低 cur-> bf =1
,表示当前cur节点的右高左低,此时新插入的节点对树的平衡因子变化为“折线”。
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curR = cur->_right;//此时不可能为空,因为右子树高
int bf = curR->_bf;//保存一份平衡因子
RotateL(cur);//先左旋
RotateR(parent);//再右旋
//左旋、右旋会将平衡因子全部处理成0,因此要对平衡因子进行更改
if (bf == 1)//在curR的右侧插入
{
curR->_bf = 0;
cur->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)//在curR左侧插入
{
curR->_bf = 0;
cur->_bf = 0;
parent->_bf = 1;
}
}
右左双旋
右左双旋的情况和左右双旋类似:
判断条件:
- 当
parent -> bf=2
时,此时表示右高左低 cur-> bf =-1
,表示当前cur节点的左高右低,此时新插入的节点对树的平衡因子变化为“折线”。
//右左双旋
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curL = cur->_left;
int bf = curL->_bf;
RotateR(cur);//先右旋
RotateL(parent);//再左旋
//平衡因子出来
if (bf == 1)//在subRL右侧插入时
{
curL->_bf = 0;
parent->_bf = -1;
cur->_bf = 0;
}
else if (bf == -1)//在左侧插入时
{
curL->_bf = 0;
parent->_bf = 0;
cur->_bf = 1;
}
}
2.2. 节点类的创建以及构造函数(KV模型)
//单个节点
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode(const pair<K, V>& kv = pair<K, V>())
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
, _kv(kv)
{}
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;//balance factor 平衡因子
pair<K, V> _kv;
};
//AVL树
template<class K, class V>
class AVLTree
{
public:
typedef struct AVLTreeNode<K, V> Node;
private:
Node* _root = nullptr;
};
pair将2个数据组合成一组数据, pair的实现是一个结构体,主要的两个成员变量是first和second,因为是使用struct不是class,所以可以直接使用pair的成员变量。
2.3. 插入
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 (kv.first < cur->_kv.first)//K值比较,小于往左边走
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)//往右走
{
parent = cur;
cur = cur->_right;
}
else//相等,不进行插入
{
return false;
}
}
//此时已经找到插入的位置了,判断插入在parent的左边还是右边
cur = new Node(kv);
if (parent->_kv.first > kv.first)//插在左边
{
parent->_left = cur;
cur->_parent = parent;//三叉链,cur父指针回指
}
else//插在右边
{
parent->_right = cur;
cur->_parent = parent;//三叉链,cur父指针回指
}
//更新平衡因子
while (parent)//不为空
{
if (parent->_left == cur)//cur在parent左侧
{
parent->_bf--;
}
else//cur在parent右侧
{
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)//左边高
{
if (cur->_bf == -1)//在当前节点的左侧插入了节点 ->右单旋
{
RotateR(parent);
}
else//cur->_bf=1 ->平衡因子为折线,需要进行左右双旋
{
RotateLR(parent);
}
}
else//右边高
{
if (cur->_bf == 1)//在当前节点的右侧插入了节点 -> 左单旋
{
RotateL(parent);
}
else//cur->_bf=-1 平衡因子为折线,需要进行右左双旋
{
RotateRL(parent);
}
}
break;//旋转过后当前的树就是平衡的了,退出
}
else//不可能走到这一步,走到这里说明程序发生了逻辑错误
{
exit(-1);
}
}
return true;
}
2.4. 删除
删除操作和插入操作有些类似:
-
右边插入,父亲平衡因子++,左边插入,父亲平衡因子–
右边删除,父亲平衡因子–,左边删除,父亲平衡因子++ -
插入后,父亲的平衡因子变为0,说明父亲所在的树高度不变,更新结束
删除后,父亲的平衡因子变为0,说明父亲所在的树高度变了(因为删除前是1或-1),继续往上更新 -
插入后,父亲的平衡因子变为1或-1,说明父亲所在的树的高度变了,继续往上更新
删除后,父亲的平衡因子变为1或-1,说明父亲所在的树的高度不变,更新结束 -
插入/删除后,父亲的平衡因子变为2或-2,说明不平衡,需要进行旋转
不过旋转的时候,cur的平衡因子有三种情况,分别为1,0,-1,当cur的平衡因子为1或-1时,可以调用上面的四种旋转情况,但是当cur为0时,上面的四种情况就行不通了,原因是旋转后的平衡因子不匹配,此时左旋或右旋以后需要手动更新其平衡因子:
cur为0时旋转完并手动更新完平衡因子后,此时整棵树的高度并没有变化,因此不需要向上更新平衡因此。
其他情况由于旋转操作使树的高度发生了改变,因此旋转会影响其父结点的平衡因子,因此我们还需要继续往上更新平衡因子。
下面是删除的函数:
//删除函数
bool Erase(const K& key)
{
//用于遍历二叉树
Node* parent = nullptr;
Node* cur = _root;
//用于标记实际的删除结点及其父结点
Node* delParentPos = nullptr;
Node* delPos = nullptr;
while (cur)
{
if (key < cur->_kv.first) //所给key值小于当前结点的key值
{
//往该结点的左子树走
parent = cur;
cur = cur->_left;
}
else if (key > cur->_kv.first) //所给key值小于当前结点的key值
{
//往该结点的右子树走
parent = cur;
cur = cur->_right;
}
else //找到了待删除结点
{
if (cur->_left == nullptr) //待删除结点的左子树为空
{
if (cur == _root) //待删除结点是根结点
{
_root = _root->_right; //让根结点的右子树作为新的根结点
//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断
if (_root)
_root->_parent = nullptr;
delete cur; //删除原根结点
return true; //根结点无祖先结点,无需进行平衡因子的更新操作
}
else
{
delParentPos = parent; //标记实际删除结点的父结点
delPos = cur; //标记实际删除的结点
}
break; //删除结点有祖先结点,需更新平衡因子
}
else if (cur->_right == nullptr) //待删除结点的右子树为空
{
if (cur == _root) //待删除结点是根结点
{
_root = _root->_left; //让根结点的左子树作为新的根结点
//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断
if (_root)
_root->_parent = nullptr;
delete cur; //删除原根结点
return true; //根结点无祖先结点,无需进行平衡因子的更新操作
}
else
{
delParentPos = parent; //标记实际删除结点的父结点
delPos = cur; //标记实际删除的结点
}
break; //删除结点有祖先结点,需更新平衡因子
}
else //待删除结点的左右子树均不为空
{
//替换法删除
//寻找待删除结点右子树当中key值最小的结点作为实际删除结点
Node* minParent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
minParent = minRight;
minRight = minRight->_left;
}
cur->_kv.first = minRight->_kv.first; //将待删除结点的key改为minRight的key
cur->_kv.second = minRight->_kv.second; //将待删除结点的value改为minRight的value
delParentPos = minParent; //标记实际删除结点的父结点
delPos = minRight; //标记实际删除的结点
break; //删除结点有祖先结点,需更新平衡因子
}
}
}
if (delParentPos == nullptr) //delParentPos没有被修改过,说明没有找到待删除结点
{
return false;
}
//记录待删除结点及其父结点
Node* del = delPos;
Node* delP = delParentPos;
//更新平衡因子
while (delPos != _root)
{
//判断删除的是在父亲的哪一边,然后更新平衡因子
if (delPos == delParentPos->_left)
{
delParentPos->_bf++;
}
else if (delPos == delParentPos->_right)
{
delParentPos->_bf--;
}
//当前树是平衡的,停止更新
if (delParentPos->_bf == -1 || delParentPos->_bf == 1)
{
break; //delParent树的高度没有发生变化,不会影响其父结点及以上结点的平衡因子
}
else if (delParentPos->_bf == 0)//需要继续往上更新平衡因子
{
//delParentPos树的高度变化,会影响其父结点的平衡因子,需要继续往上更新平衡因子
delPos = delParentPos;
delParentPos = delParentPos->_parent;
}
else if (delParentPos->_bf == -2 || delParentPos->_bf == 2) //需要进行旋转处理
{
if (delParentPos->_bf == -2)//左边高
{
if (delParentPos->_left->_bf == -1)//左边高,进行右单旋
{
Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点
RotateR(delParentPos); //右单旋
delParentPos = tmp; //更新根结点
}
else if (delParentPos->_left->_bf == 1)//曲线影响,需要进行左右双旋
{
Node* tmp = delParentPos->_left->_right; //记录delParentPos左右旋转后新的根结点
RotateLR(delParentPos); //左右双旋
delParentPos = tmp; //更新根结点
}
else //delParentPos->_left->_bf == 0
{
Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点
RotateR(delParentPos); //右单旋
delParentPos = tmp; //更新根结点
//平衡因子调整
delParentPos->_bf = 1;
delParentPos->_right->_bf = -1;
break;
}
}
else //delParentPos->_bf == 2
{
if (delParentPos->_right->_bf == -1)
{
Node* tmp = delParentPos->_right->_left; //记录delParentPos右左旋转后新的根结点
RotateRL(delParentPos); //右左双旋
delParentPos = tmp; //更新根结点
}
else if (delParentPos->_right->_bf == 1)//曲线影响,需要进行左右双旋
{
Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点
RotateL(delParentPos); //左单旋
delParentPos = tmp; //更新根结点
}
else //delParentPos->_right->_bf == 0
{
Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点
RotateL(delParentPos); //左单旋
delParentPos = tmp; //更新根结点
//平衡因子调整
delParentPos->_bf = -1;
delParentPos->_left->_bf = 1;
break;
}
}
//继续往上更新平衡因子
delPos = delParentPos;
delParentPos = delParentPos->_parent;
}
else
{
exit(-1); //不会到这里,到这里说明出现了错误
}
}
//进行实际删除
if (del->_left == nullptr) //实际删除结点的左子树为空
{
if (del == delP->_left) //实际删除结点是其父结点的左孩子
{
delP->_left = del->_right;
if (del->_right)
del->_right->_parent = parent;
}
else //实际删除结点是其父结点的右孩子
{
delP->_right = del->_right;
if (del->_right)
del->_right->_parent = parent;
}
}
else //实际删除结点的右子树为空
{
if (del == delP->_left) //实际删除结点是其父结点的左孩子
{
delP->_left = del->_left;
if (del->_left)
del->_left->_parent = parent;
}
else //实际删除结点是其父结点的右孩子
{
delP->_right = del->_left;
if (del->_left)
del->_left->_parent = parent;
}
}
delete del; //实际删除结点
return true;
}
三、三叉链AVL树的代码以及测试
#pragma once
using namespace std;
#include <iostream>
#include<queue>
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode(const pair<K, V>& kv = pair<K, V>())
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
, _kv(kv)
{}
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;//balance factor 平衡因子
pair<K, V> _kv;
};
template<class K, class V>
class AVLTree
{
public:
typedef struct AVLTreeNode<K, V> Node;
//右单旋
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curR = cur->_right;//cur的右子树
Node* pparent = parent->_parent;//保存parent的父亲节点
//将cur右子树链接到parent的左侧
parent->_left = curR;
if (curR)
curR->_parent = parent;
//将parent连接到cur的右侧
cur->_right = parent;
parent->_parent = cur;
//将cur与pparent链接起来
if (pparent == nullptr)//cur变成新的根
{
_root = cur;
cur->_parent = nullptr;
}
else//pparent不为根
{
cur->_parent = pparent;
if (parent == pparent->_left)//parent在父亲节点的左侧
{
pparent->_left = cur;
}
else
{
pparent->_right = cur;
}
}
//平衡因子更新
parent->_bf = 0;
cur->_bf = 0;
}
//左单旋
void RotateL(Node* parent)//左旋
{
Node* cur = parent->_right;//右变高,不可能为空
Node* curL = cur->_left;
Node* pprent = parent->_parent;
//curL连接到parent上
parent->_right = curL;
if (curL)
curL->_parent = parent;
//parent连接到cur上
cur->_left = parent;
parent->_parent = cur;
//cur链接到pprent上
if (pprent == nullptr)//根
{
_root = cur;
cur->_parent = nullptr;
}
else//不为根
{
cur->_parent = pprent;
//判断链接在哪一侧
if (pprent->_left == parent)
{
pprent->_left = cur;
}
else
{
pprent->_right = cur;
}
}
//平衡因子的更新
parent->_bf = 0;
cur->_bf = 0;
}
//左右双旋
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curR = cur->_right;//此时不可能为空,因为右子树高
int bf = curR->_bf;//保存一份平衡因子
RotateL(cur);//先左旋
RotateR(parent);//再右旋
//左旋、右旋会将平衡因子全部处理成0,因此要对平衡因子进行更改
if (bf == 1)//在curR的右侧插入
{
curR->_bf = 0;
cur->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)//在curR左侧插入
{
curR->_bf = 0;
cur->_bf = 0;
parent->_bf = 1;
}
}
//右左双旋
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curL = cur->_left;
int bf = curL->_bf;
RotateR(cur);//先右旋
RotateL(parent);//再左旋
//平衡因子出来
if (bf == 1)//在subRL右侧插入时
{
curL->_bf = 0;
parent->_bf = -1;
cur->_bf = 0;
}
else if (bf == -1)//在左侧插入时
{
curL->_bf = 0;
parent->_bf = 0;
cur->_bf = 1;
}
}
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 (kv.first < cur->_kv.first)//K值比较,小于往左边走
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)//往右走
{
parent = cur;
cur = cur->_right;
}
else//相等,不进行插入
{
return false;
}
}
//此时已经找到插入的位置了,判断插入在parent的左边还是右边
cur = new Node(kv);
if (parent->_kv.first > kv.first)//插在左边
{
parent->_left = cur;
cur->_parent = parent;//三叉链,cur父指针回指
}
else//插在右边
{
parent->_right = cur;
cur->_parent = parent;//三叉链,cur父指针回指
}
//更新平衡因子
while (parent)//不为空
{
if (parent->_left == cur)//cur在parent左侧
{
parent->_bf--;
}
else//cur在parent右侧
{
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)//左边高
{
if (cur->_bf == -1)//是在当前节点的左侧插入了节点 ->右单旋
{
RotateR(parent);
}
else//cur->_bf=1 ->曲线影响,需要进行左右双旋
{
RotateLR(parent);
}
}
else//右边高
{
if (cur->_bf == 1)//在当前节点的右侧插入了节点 -> 左单旋
{
RotateL(parent);
}
else//cur->_bf=-1 曲线影响
{
RotateRL(parent);
}
}
break;//旋转过后当前的树就是平衡的了,退出
}
else//0 1 2 -> 不可能走到这一步,走到这里说明发生了逻辑错误
{
exit(-1);
}
}
return true;
}
//删除函数
bool Erase(const K& key)
{
//用于遍历二叉树
Node* parent = nullptr;
Node* cur = _root;
//用于标记实际的删除结点及其父结点
Node* delParentPos = nullptr;
Node* delPos = nullptr;
while (cur)
{
if (key < cur->_kv.first) //所给key值小于当前结点的key值
{
//往该结点的左子树走
parent = cur;
cur = cur->_left;
}
else if (key > cur->_kv.first) //所给key值小于当前结点的key值
{
//往该结点的右子树走
parent = cur;
cur = cur->_right;
}
else //找到了待删除结点
{
if (cur->_left == nullptr) //待删除结点的左子树为空
{
if (cur == _root) //待删除结点是根结点
{
_root = _root->_right; //让根结点的右子树作为新的根结点
//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断
if (_root)
_root->_parent = nullptr;
delete cur; //删除原根结点
return true; //根结点无祖先结点,无需进行平衡因子的更新操作
}
else
{
delParentPos = parent; //标记实际删除结点的父结点
delPos = cur; //标记实际删除的结点
}
break; //删除结点有祖先结点,需更新平衡因子
}
else if (cur->_right == nullptr) //待删除结点的右子树为空
{
if (cur == _root) //待删除结点是根结点
{
_root = _root->_left; //让根结点的左子树作为新的根结点
//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断
if (_root)
_root->_parent = nullptr;
delete cur; //删除原根结点
return true; //根结点无祖先结点,无需进行平衡因子的更新操作
}
else
{
delParentPos = parent; //标记实际删除结点的父结点
delPos = cur; //标记实际删除的结点
}
break; //删除结点有祖先结点,需更新平衡因子
}
else //待删除结点的左右子树均不为空
{
//替换法删除
//寻找待删除结点右子树当中key值最小的结点作为实际删除结点
Node* minParent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
minParent = minRight;
minRight = minRight->_left;
}
cur->_kv.first = minRight->_kv.first; //将待删除结点的key改为minRight的key
cur->_kv.second = minRight->_kv.second; //将待删除结点的value改为minRight的value
delParentPos = minParent; //标记实际删除结点的父结点
delPos = minRight; //标记实际删除的结点
break; //删除结点有祖先结点,需更新平衡因子
}
}
}
if (delParentPos == nullptr) //delParentPos没有被修改过,说明没有找到待删除结点
{
return false;
}
//记录待删除结点及其父结点
Node* del = delPos;
Node* delP = delParentPos;
//更新平衡因子
while (delPos != _root)
{
//判断删除的是在父亲的哪一边,然后更新平衡因子
if (delPos == delParentPos->_left)
{
delParentPos->_bf++;
}
else if (delPos == delParentPos->_right)
{
delParentPos->_bf--;
}
//当前树是平衡的,停止更新
if (delParentPos->_bf == -1 || delParentPos->_bf == 1)
{
break; //delParent树的高度没有发生变化,不会影响其父结点及以上结点的平衡因子
}
else if (delParentPos->_bf == 0)//需要继续往上更新平衡因子
{
//delParentPos树的高度变化,会影响其父结点的平衡因子,需要继续往上更新平衡因子
delPos = delParentPos;
delParentPos = delParentPos->_parent;
}
else if (delParentPos->_bf == -2 || delParentPos->_bf == 2) //需要进行旋转处理
{
if (delParentPos->_bf == -2)//左边高
{
if (delParentPos->_left->_bf == -1)//左边高,进行右单旋
{
Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点
RotateR(delParentPos); //右单旋
delParentPos = tmp; //更新根结点
}
else if (delParentPos->_left->_bf == 1)//曲线影响,需要进行左右双旋
{
Node* tmp = delParentPos->_left->_right; //记录delParentPos左右旋转后新的根结点
RotateLR(delParentPos); //左右双旋
delParentPos = tmp; //更新根结点
}
else //delParentPos->_left->_bf == 0
{
Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点
RotateR(delParentPos); //右单旋
delParentPos = tmp; //更新根结点
//平衡因子调整
delParentPos->_bf = 1;
delParentPos->_right->_bf = -1;
break;
}
}
else //delParentPos->_bf == 2
{
if (delParentPos->_right->_bf == -1)
{
Node* tmp = delParentPos->_right->_left; //记录delParentPos右左旋转后新的根结点
RotateRL(delParentPos); //右左双旋
delParentPos = tmp; //更新根结点
}
else if (delParentPos->_right->_bf == 1)//曲线影响,需要进行左右双旋
{
Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点
RotateL(delParentPos); //左单旋
delParentPos = tmp; //更新根结点
}
else //delParentPos->_right->_bf == 0
{
Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点
RotateL(delParentPos); //左单旋
delParentPos = tmp; //更新根结点
//平衡因子调整
delParentPos->_bf = -1;
delParentPos->_left->_bf = 1;
break;
}
}
//继续往上更新平衡因子
delPos = delParentPos;
delParentPos = delParentPos->_parent;
}
else
{
exit(-1); //不会到这里,到这里说明出现了错误
}
}
//进行实际删除
if (del->_left == nullptr) //实际删除结点的左子树为空
{
if (del == delP->_left) //实际删除结点是其父结点的左孩子
{
delP->_left = del->_right;
if (del->_right)
del->_right->_parent = parent;
}
else //实际删除结点是其父结点的右孩子
{
delP->_right = del->_right;
if (del->_right)
del->_right->_parent = parent;
}
}
else //实际删除结点的右子树为空
{
if (del == delP->_left) //实际删除结点是其父结点的左孩子
{
delP->_left = del->_left;
if (del->_left)
del->_left->_parent = parent;
}
else //实际删除结点是其父结点的右孩子
{
delP->_right = del->_left;
if (del->_left)
del->_left->_parent = parent;
}
}
delete del; //实际删除结点
return true;
}
//遍历的时候 root为private外面无法拿到
//因此需要封装一层
void _Inorder(Node* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_kv.first << " " << root->_kv.second << endl;
_Inorder(root->_right);
}
//中序遍历
void Inorder()
{
_Inorder(_root);
}
Node* Find(const K& k)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first > k)
cur = cur->_left;
else if (cur->_kv.first < k)
cur = cur->_right;
else
return cur;
}
return false;
}
//求树的深度
int maxDepth(Node* root)
{
if (root == NULL)
{
return 0;
}
int leftDepth = maxDepth(root->_left);
int rightDepth = maxDepth(root->_right);
return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
bool _isBalanced(Node* root)
{
if (root == nullptr)
{
return true;
}
int leftHight = maxDepth(root->_left);
int rightHight = maxDepth(root->_right);
return abs(leftHight - rightHight) < 2
&&_isBalanced(root->_left)
&&_isBalanced(root->_right);
}
bool isBalanced()
{
return _isBalanced(_root);
}
private:
Node* _root = nullptr;
};
四、二叉链的AVL树(严蔚敏版)(K模型)
严蔚敏版的二叉链AVL树的平衡因子采用的是左减右的方式。
由于是二叉链,没有父亲节点,这就导致没法直接向上调节平衡因子,严蔚敏给出的思路是采用递归的方式。
这里直接给出代码:
#include <iostream>
using namespace std;
#define LH +1
#define EH 0
#define RH -1
typedef struct BSTNode
{
int data;
int bf;
struct BSTNode* lchild, * rchild;
}BSTNode, * BSTree;
void R_Rotate(BSTree& p)
{
BSTree lc;
lc = p->lchild;
p->lchild = lc->rchild;
lc->rchild = p;
p = lc;
return;
}
void L_Rotate(BSTree& p)
{
BSTree rc;
rc = p->rchild;
p->rchild = rc->lchild;
rc->lchild = p;
p = rc;
return;
}
void LeftBalance(BSTree& T)
{
//对指针T所致节点为根的二叉树作平衡旋转处理
BSTree lc;
lc = T->lchild;
//检查T节点的左孩子,根据其平衡因子判断是右旋还是左右双旋
switch (lc->bf)
{
//左孩子的平衡因子为1,平衡因子是直线,右旋
case LH:
T->bf = EH;
lc->bf = EH;
R_Rotate(T);
break;
//左孩子平衡因子为-1,平衡因子为折线,左右双旋
case RH:
BSTree rd;
rd = lc->rchild;
//修改T节点和其左孩子的平衡因子
switch (rd->bf)
{
case LH:
T->bf = RH;
lc->bf = EH;
break;
case EH:
T->bf = lc->bf = EH;
break;
case RH:
lc->bf = LH;
T->bf = EH;
break;
}
rd->bf = EH;
L_Rotate(T->lchild);
R_Rotate(T);
}
}
void RightBalance(BSTree& T)
{
//对指针T所致节点为根的二叉树作平衡旋转处理
BSTree rc;
rc = T->rchild;
//检查T节点的右孩子,根据其平衡因子判断是左旋还是右左双旋
switch (rc->bf)
{
//右孩子的平衡因子为-1,平衡因子是直线,左旋
case RH:
T->bf = EH;
rc->bf = EH;
L_Rotate(T);
break;
//右孩子平衡因子为-1,平衡因子为折线,右左双旋
case LH:
BSTree ld;
ld = rc->lchild;
//修改T节点和其右孩子的平衡因子
switch (ld->bf)
{
case LH:
T->bf = EH;
rc->bf = RH;
break;
case EH:
T->bf = rc->bf = EH;
break;
case RH:
T->bf = LH;
rc->bf = EH;
break;
}
ld->bf = EH;
R_Rotate(T->rchild);
L_Rotate(T);
}
}
int InsertAVL(BSTree& T, int e, bool& taller)
{
if (!T)
{
T = (BSTree)malloc(sizeof(BSTNode));
T->data = e;
T->lchild = NULL;
T->rchild = NULL;
T->bf = EH;
taller = true;
}
else
{
//树中已存在和e相同的节点,返回0
if (T->data == e)
{
taller = false;
return 0;
}
//继续在左子树中搜索
if (T->data > e)
{
//说明递归插入失败了
if (!InsertAVL(T->lchild, e, taller))
{
taller = false;
return 0;
}
//到这里说明插入成功,判断平衡因子
if (taller)
{
switch (T->bf)
{
//原本左子树比右子树高,再插入以后,平衡因子变为2,此时需要做左平衡处理
case LH:
LeftBalance(T);
taller = false;
break;
//原本左右子树,等高,现因左子树增高而使树增高
case EH:
taller = true;
T->bf = LH;
break;
//原本右子树比左子树高,现在左右子树登高
case RH:
taller = false;
T->bf = EH;
break;
}
}
}
//继续在右子树中搜索
else
{
if (!InsertAVL(T->rchild, e, taller))
{
taller = false;
return 0;
}
//到这里说明插入成功,判断平衡因子
if (taller)
{
switch (T->bf)
{
//原本左子树比右子树高,插入以后,左右等高
case LH:
T->bf = EH;
taller = false;
break;
//原本等高,插入以后,右子树等高
case EH:
T->bf = RH;
taller = true;
break;
//原本右子树高,插入以后,平衡因子变为-2,需要做右平衡处理
case RH:
RightBalance(T);
taller = false;
break;
}
}
}
}
return 1;
}
//中序遍历
void InorderTraversal(BSTree node)
{
if (!node)
{
return;
}
else
{
InorderTraversal(node->lchild);
cout << node->data << " ";
InorderTraversal(node->rchild);
}
return;
}
int main(void)
{
//flag用于判断是否插入成功,ture为成功,false为失败
bool flag = false;
BSTree T = NULL;
InsertAVL(T, 7, flag);
InsertAVL(T, 8, flag);
InsertAVL(T, 9, flag);
InsertAVL(T, 4, flag);
InsertAVL(T, 5, flag);
InsertAVL(T, 6, flag);
InsertAVL(T, 1, flag);
InsertAVL(T, 2, flag);
InsertAVL(T, 3, flag);
InorderTraversal(T);
cout << endl;
return 0;
}