目录
AVL树简介
我们在前面的学习中会知道,二叉搜索树有一个弊端,就是可能会一直插入在一条线上,如下:
当我们遇到这种情况的时候,其实就和链表差不多了,效率就会从logN变成O(N)
换句话说就是,100万个数据从搜索20次变成100万次
所以就有了AVL树,这个树有一个规定就是:左右节点的高度差不超过1
如上,这就是一棵AVL树,我们看到节点3,左子树高度为1,右子树为2,高度差为1
我们看到节点8,左子树高3,右子树为2,高度差为1
并且,AVL树我们可以理解成是一棵增加了AVL特性的二叉搜索树
因为AVL树也是规定,左子树比根节点小,右子树比根节点大,所以我们中序遍历时就是从小到大的一个顺序
AVL树框架构建
AVL树节点类
首先,我们的AVL树这个类的成员变量就只有一个,就是AVL树的节点
但是一个节点需要有左右指针,还要有一个指针指向parent,还要有平衡因子和一个类型为pair的值
所以单靠一个变量是没法同时实现这些功能的,所以我们需要实现一个类
代码如下:
template<class k, class v>
struct AVLTreeNode
{
AVLTreeNode<k, v>* _left;
AVLTreeNode<k, v>* _right;
AVLTreeNode<k, v>* _parent;
int _bf;
pair<k, v> _kv;
AVLTreeNode(const pair<k, v>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
,_bf(0)
,_kv(kv)
{}
};
AVL树类框架构建
首先,我们的类只有一个成员变量就是AVL树节点
然后,我们还需要一个模板,这样我们就可以控制pair的两个值
最后,我们还可以给这个节点的类名typedef一下,这样我们后续会好写一点
代码如下:
template<class k, class v>
class AVLTree
{
typedef AVLTreeNode<k, v> Node;
public:
private:
Node* _root;
};
构造与拷贝构造
构造
我们的构造斌不需要显示写,因为只有一个成员变量,这个成员还是自定义类型,默认生成的构造函数会自动调用他的构造函数
但是,我们后面还要写拷贝构造,默认构造生成的条件是不能有构造,拷贝构造也是构造
所以,我们可以直接使用default这种做法来强制编译器默认生成一个
代码如下:
AVLTree() = default;
拷贝构造
我们的拷贝构造就只能老老实实,一个一个拷贝了
这时我们可以写一个递归来实现拷贝
递归的主逻辑:首先,递归出口是当传过来的节点为空,就返回空指针
然后是new一个节点,这个节点的左子树链接到一个递归,右子树一个递归,最后返回这个新插入的节点即可,就不再赘述了
AVLTree(const AVLTree<k, v>& avl)
{
_root = copy(avl._root);
}
Node* copy(Node* root)
{
if (root == nullptr)return nullptr;
Node* newnode = new Node(root->_kv);
newnode->_right = copy(root->_right);
newnode->_left = copy(root->_left);
return newnode;
}
析构函数
析构的大逻辑还是递归,具体就是,如果传过来的节点为空,就直接return
然后就是先走左,再走右,最后删除传过来的节点
代码如下:
~AVLTree()
{
Destroy(_root);
_root = nullptr;
}
void Destroy(Node* root)
{
if (root == nullptr)return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
Insert 插入
我们的AVL树引入了一个平衡因子的概念,就是每一个节点都有这么一个成员,而具体算法就是:
右子树的高度 - 左子树的高度
而我们要插入的话,我们得先找一找,这棵树里面有没有一样的值,如果有的话,那我还插入什么,直接return即可
要是如果找不到一样的值的话,那我们也就找到了待插入的位置,因为一直找不到,直到为空就退出,那么我们退出的时候,那个节点就是待插入的位置
但是我们光找到这个没有用,我们new完节点之后,还需要将其与父节点链接起来,所以我们同时还需要再定义一个变量用来记录父节点的位置
目前代码如下:
bool Insert(const pair<k, v>& kv)
{
if (!_root)
{
_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;
}
}
接下来就是需要new一个cur节点出来,然后我们再根据这个节点里面的值与父节点里面的值的大小关系,将这个新节点和父节点链接起来
如果cur的值比父节点的值小,就插入在父节点的左边,反之则是右边
最后控制一下新节点的parent指针即可,此段代码如下:
cur = new Node(kv);
if (cur->_kv.first < parent->_kv.first)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
然后我们就进入了平衡因子的调整环节
首先我们需要写一个while循环,因为我们可能会一直向上调整,所以需要一个循环,循环的条件就是parent还存在,如果parent == nullptr的话,就证明已经到根节点了,没法旋了,就可以退出了
然后控制一下平衡因子,如果新节点插入在parent的左边,那我们的parent的平衡因子就需要--,因为是右子树的高度 - 左子树的高度,所以在左的话就增加了左的高度,就需要--
反之,在右边就是++
此段如下:
while (parent)
{
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;
}
然后就是判断需不需要旋转了,这可以分为一下三种情况:
- 父节点的平衡因子为0,证明左右两边高度相同,不需要旋转啥的,直接返回即可(或者说插入的地方本来是只有一个节点的,插入之后变成两个节点,整体高度没变,直接返回即可)
- 父节点的平衡因子为1或者-1,就说明可能是在原本为0的地方插入了一个节点,这棵子树虽然不用旋转,但是整体高度+1了,上面的节点可能就不平衡了,所以我们需要向上调整,也就是cur = parent, parent = parent->_parent;
- 父节点的平衡因子等于2或者-2,已经严重不平衡了,需要旋转处理
(为0的情况)
(为1或-1的情况)
(为2或-2的情况)
代码如下:
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)
{
旋转逻辑
}
else
{
assert(false);
}
插入里面的旋转逻辑在下文,讲完旋转之后就是剩下为2或者-2的情况了
四大旋转介绍
总体情况介绍
接下来我们来盘点一下,插入中旋转的四种情况:
(右单旋)
(左单旋)
(右左双旋)
右左双旋就是:
下图为例:如果父节点的平衡因子与子节点的平衡因子异号的话,我们对下图左的30进行一次左单旋的话,会发现依然是不平衡,再对其进行一次右单旋会发现,又变回来了
所以我们先对下图中的节点30进行一次右单旋,会发现其就直接变成了对10节点左单旋的情况
所以这叫做右左双旋
(左右双旋)
就是右左的镜像处理
左单旋
左单旋的代码如下:(解析在代码下面)
//左单旋
void RotateL(Node* parent)
{
///
Node* curR = parent->_right;
Node* curRL = curR->_left;
Node* Parentparent = parent->_parent;
//旋转逻辑
parent->_right = curRL;
curR->_left = parent;
///
//处理parent
if (curRL)curRL->_parent = parent;
parent->_parent = curR;
///
if (!Parentparent)
{
_root = curR;
curR->_parent = nullptr;
}
else
{
if (Parentparent->_left == parent)
Parentparent->_left = curR;
else
Parentparent->_right = curR;
curR->_parent = Parentparent;
}
///
curR->_bf = parent->_bf = 0;
///
}
这代码其实很简单,不要将其想得太复杂,首先我们是先将三个节点定义了出来
其中第三个是parent的_parent,我们将其命名为Parentparent
然后将curRL变成parent的右节点
然后parent自己成为curR的左节点
因为本来就是curR那边多出来了,我们旋转一下之后,parent下去了,那么自然就平衡了
(上述为第一块代码区域)
而后面的代码就都是在干一件事:找父节点
因为我们的节点是有三个指针的,一个左,一个右,一个parent
我们接下来解决的就是parent指针的指向
首先是curRL,因为我们不确定其是否为空,所以我们需要判断一下,如果存在,再让其的parent指针指向parent
(上述为第二块代码区域)
然后是parent,parent的_parent指针指向的是curR
最后是curR,我们无法确定Parentparent是否为空,因为如果parent节点旋转之前是根节点呢
所以我们就加了一条判断逻辑(如果为空,就直接让curR做根节点,并将curR的parent节点置空)
然后,如果Parentparent不为空,因为Parentparent还指向着parent节点,所以我们就直接判断curR是要链接在Parentparent的左边还是右边
最后让curR的parent指针指向Parentparent
(上述为第三块代码区域)
最后的最后就是平衡因子的处理,单旋的两种情况,都是旋转完之后平衡因子都变成0,所以我们直接置为0即可
(上述为第四块代码区域)
右单旋
右单旋就是左单选的镜像处理,这里就不再赘述了
//右单旋
void RotateR(Node* parent)
{
Node* curL = parent->_left;
Node* curLR = curL->_right;
Node* Parentparent = parent->_parent;
//旋转逻辑
parent->_left = curLR;
curL->_right = parent;
//处理parent
if (curLR)curLR->_parent = parent;
parent->_parent = curL;
if (!Parentparent)
{
_root = curL;
curL->_parent = nullptr;
}
else
{
if (Parentparent->_left == parent)
Parentparent->_left = curL;
else
Parentparent->_right = curL;
curL->_parent = Parentparent;
}
curL->_bf = parent->_bf = 0;
}
右左双旋
代码如下,解析在代码下面:
//右左双旋
void RotateRL(Node* parent)
{
Node* curR = parent->_right;
Node* curRL = curR->_left;
int bf = curRL->_bf;
RotateR(curR);
RotateL(parent);
if (bf == 0)
{
parent->_bf = 0;
curR->_bf = 0;
curRL->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = -1;
curR->_bf = 0;
curRL->_bf = 0;
}
else if(bf == -1)
{
parent->_bf = 0;
curR->_bf = 1;
curRL->_bf = 0;
}
else
{
assert(false);
}
}
右左双旋最关键的,就是最下面那个节点的平衡因子的值
我们看图,就这么去抽象地想,旋转完之后,curRL节点从最下面跑到了最上面
然后parent和curR就分居在curRL的左右两侧
然后curRL的左子树和右子树,分别给了parent当右子树,给了curR当左子树
所以,curRL的平衡因子,就决定了最后的平衡因子的调整
综上,我们在定义出curR和curRL的同时,我们还需要将curRL的平衡因子积累下来
接着就是先对curR进行右旋,然后再对parent进行左旋操作
最后,分三种情况讨论,bf为0、bf为1,bf为-1(bf是curRL的平衡因子)
左右双旋
左右双旋就是右左双旋的镜像,这里就不再赘述了
代码如下:
//左右双旋
void RotateLR(Node* parent)
{
Node* curL = parent->_left;
Node* curLR = curL->_right;
int bf = curLR->_bf;
RotateL(curL);
RotateR(parent);
if (bf == 0)
{
parent->_bf = 0;
curL->_bf = 0;
curLR->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = 0;
curL->_bf = -1;
curLR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
curL->_bf = 0;
curLR->_bf = 0;
}
else
{
assert(false);
}
}
Insert 插入(旋转逻辑补全版)
上面,我们刚讲到要旋转的地方就停止了,接着就是讲解旋转的四种情况
接下来,我们来讲一讲插入时要遇到的旋转逻辑:
如果父节点的平衡因子为 2,子节点的平衡因子为 1,就左单旋
如果父节点的平衡因子为 -2,子节点的平衡因子为 -1,就右单旋
如果父节点的平衡因子为 2,子节点的平衡因子为 -1,就右左双旋
如果父节点的平衡因子为 -2,子节点的平衡因子为 1,就左右双旋
代码如下:
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
{
//左右双旋
RotateLR(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
插入完整代码如下:
bool Insert(const pair<k, v>& kv)
{
if (!_root)
{
_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 (cur->_kv.first < parent->_kv.first)
parent->_left = cur;
else
parent->_right = 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
{
//左右双旋
RotateLR(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
AVL树删除算法
提示,在讲删除算法之前,需要理解二叉搜索树中的删除逻辑,下文虽然有讲解,但是不会那么详细,建议不熟的可以看一看下面这篇讲解二叉搜索树的博客:
【STL】二叉搜索树 BSTree(底层讲解 + key_value(KV)的引入)
接下来我们来正式开始讲解AVL树的删除
在开始删除之前,我们需要先找到这个待删除的节点,所以我们需要写一个循环,找到这个节点
但是我们还是需要写一个parent节点,因为删除了节点之后,我们还要和被删除节点的孩子节点链接起来
我们先将前面的代码实现出来(找待删除节点):
bool Erase(const k& key)
{
if (!_root)return false;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
parent = cur;
cur = cur->_left;
}
else
{
//
// 删除主逻辑
//
}
}
return false;
}
删除里面还需要分三种请款
第一种是待删除节点没有孩子
第二种情况是待删除节点只有一个孩子
第三种情况是待删除节点有两个孩子
面对第一种情况,我们直接删除,然后让父节点指向这个节点的指针置空即可
面对第二种情况,我们就需要让父节点指向这个节点的孩子节点,随后才能将这个待删除节点删除掉
面对第三种情况,我们就需要转换问题,先去找左子树的最右节点、或者是右子树的最左节点(这里以右子树的最左举例) 然后我们用这个节点的值覆盖待删除节点的值,接着我们就将问题从删除待删除节点——>删除待删除节点的右子树的最左节点
而我们右子树的最左节点肯定是满足第一种情况或第二种情况的,按这两种情况处理即可
第一种和第二种情况其实可以合并成一种情况,我们在删除的时候,只需要判断一下左孩子节点是否为空,走一个逻辑;判断一下右孩子节点是否为空,走一个逻辑
如果没有孩子节点的话,就都会走左孩子节点为空的情况,但是里面是将右孩子交给父节点,其实就是将父节点置空,所以走哪个条件都无所谓
当我们走完了判断左孩子和右孩子之后,就顺便把第二种情况解决完了,因为只有一个孩子,不是左孩子就是右孩子
但是有两个孩子的情况我们需要单独写一个逻辑
我们需要先找到右子树的最左节点
然后将那个节点的值与待删除节点交换(转换问题,之后就变成了删除右子树的最左节点)
然后我们重置搜索待删除节点时的parent和cur
最后删除右子树的最左节点即可
上述代码如下:
// 删除主逻辑
// 判断三种情况,没孩子,一个孩子,两个孩子
if (cur->_left == nullptr)
{
// 假如删除的是根节点
if (!parent)
{
_root = cur->_right;
delete cur;
return true;
}
else
{
if (parent->_left == cur)
{
// 如果待删除节点在parent的左
// 平衡因子 ++
parent->_bf++;
parent->_left = cur->_right;
}
else
{
// 如果待删除节点在parent的右
// 平衡因子 --
parent->_bf--;
parent->_right = cur->_right;
}
}
}
else if (cur->_right == nullptr)
{
// 假如删除的是根节点
if (!parent)
{
_root = cur->_left;
delete cur;
return true;
}
else
{
if (parent->_left == cur)
{
// 如果待删除节点在parent的左
// 平衡因子 ++
parent->_bf++;
parent->_left = cur->_left;
}
else
{
// 如果待删除节点在parent的右
// 平衡因子 --
parent->_bf--;
parent->_right = cur->_left;
}
}
}
else
{
// 有两个孩子的情况
// 找右子树的最左节点
Node* rightMinP = cur;
Node* rightMin = cur->_right;
// 循环找最左节点
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
// 交换右子树的最左节点与待删除节点的值
cur->_kv = rightMin->_kv;
// 删除右子树的最左节点
if (rightMin == rightMinP->_left)
{
rightMinP->_bf++;
rightMinP->_left = rightMin->_right;
}
else
{
rightMinP->_bf--;
rightMinP->_right = rightMin->_right;
}
// 重置parent和cur
parent = rightMinP;
cur = rightMin;
}
delete cur;
注意,上述代码中加入了平衡因子调节的逻辑
并且在有两个孩子的情况的最后面,我们是将cur置为rightMin之后,统一删除cur节点的
这时cur节点就变成了野指针,所以我们不能使用
但这也有一个解决方法,就是后面调整平衡因子逻辑的时候,我们就将第一次调整给跳过,因为我们上面已经调整过了,只是第一次调整不需要
后面我们会给cur赋值之后才使用,各位不必担心
更新平衡因子和旋转逻辑
首先我们第一次是不需要更新平衡因子的,所以我们可以设一个bool变量为false,平衡因子那里就判断一下,如果为true才进入调整平衡因子,在第一次跳过之后,后面我们永久将那个bool变量置为true即可
上述代码如下:
bool is_or_not = false;
while (parent)
{
// 更新平衡因子
if (is_or_not)
{
if (parent->_left == cur)
parent->_bf++;
else
parent->_bf--;
}
is_or_not = true;
/
后续旋转逻辑
具体代码为下一段
/
}
接着我们就需要分析一下是否需要了
这时候可以分三种情况讨论:
- 父节点的平衡因子为0
- 父节点的平衡因子为1或-1
- 父节点的平衡因子为2或-2
首先我们是删除逻辑,如果删除之后,父节点的平衡因子为0,就证明之前是1或-1,就是本来只有一个节点,但是现在删除的就是那个节点,这时候,整个子树的高度已经变了,所以我们需要向上调整
然后是为1或-1的情况,如果是1或-1,只能是原本为0的情况,因为0代表有两个节点,但是现在删掉了左孩子或者右孩子,就变成了1或-1,这里只有0或2或-2能删除了一个节点之后变成1或-1,但是如果是2或-2的话代表原来本来就不平衡了,所以2或-2变成1或-1是不可能的,只能是0变成1或-1
最后是2或-2的情况,这个已经严重不平衡了,需要旋转
上述代码如下:
if (parent->_bf == 1 || parent->_bf == -1)
return true;
else if (parent->_bf == 0)
{
cur = parent;
parent = parent->_parent;
}
else if(parent->_bf == 2 || parent->_bf == -2)
{
//
此处代码为旋转
具体代码在下一段
//
}
else
{
assert(false);
}
进入了旋转环节之后,同样需要分情况讨论,我们都知道平衡因子只能是0,1,-1,2,-2
而这时父节点已经是2或-2的其中一个了
而我们的孩子节点肯定不可能是2或者-2,因为是的话代码就出问题了,孩子早就该旋转了,所以只有在我们代码写错了的情况下才会出现这种情况
所以子节点只能是0,1,-1
- 父节点为2或-2,孩子节点为0(0是一种特殊情况,调整完之后就平衡了,高度也没变,所以0的情况调整完之后就直接return退出即可)
- 父节点为2,子节点为1(同号,单旋)(左单旋)
- 父节点为-2,子节点为-1(同号,单旋)(右单旋)
- 父节点为2,子节点为-1(异号,双旋)(右左双旋)
- 父节点为-2,子节点为1(异号,双旋)(左右双旋)
上述看似有5种情况,但其实只有三种,因为中间的同号单旋算一种,异号双旋算一种
我们先来看子节点为0的情况:
接着就是同号和异号各两种情况了,剩下的这几种情况和插入那里都是一样的,同号单旋异号双旋
只不过,我们这个旋转逻辑处理完之后,我们需要自己手动向上调整,因为剩下的这几种情况删除旋转之后,整棵树的高度都会发生改变,整体的变了,上面的节点就会收到影响,所以我们就需要向上调整
一直调整到为1或-1,或者就是调整到根,才停止
代码如下:
else if(parent->_bf == 2 || parent->_bf == -2)
{
//旋转逻辑
Node* highernode;
int sign;
if (parent->_bf > 0)
{
sign = 1;
highernode = parent->_right;
}
else
{
sign = -1;
highernode = parent->_left;
}
if (highernode->_bf == 0)
{
if (highernode == parent->_right)
{
RotateL(parent);
parent->_bf = 1;
highernode->_bf = -1;
}
else
{
RotateR(parent);
parent->_bf = -1;
highernode->_bf = 1;
}
}
else if (sign == highernode->_bf)
{
// 单旋
if (sign > 0)
RotateL(parent);
else
RotateR(parent);
}
else
{
// 多旋
if (sign > 0)
RotateRL(parent);
else
RotateLR(parent);
}
cur = parent;
parent = parent->_parent;
}
删除总代码如下:
bool Erase(const k& key)
{
if (!_root)return false;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
parent = cur;
cur = cur->_left;
}
else
{
// 删除主逻辑
// 判断三种情况,没孩子,一个孩子,两个孩子
if (cur->_left == nullptr)
{
// 假如删除的是根节点
if (!parent)
{
_root = cur->_right;
delete cur;
return true;
}
else
{
if (parent->_left == cur)
{
// 如果待删除节点在parent的左
// 平衡因子 ++
parent->_bf++;
parent->_left = cur->_right;
}
else
{
// 如果待删除节点在parent的右
// 平衡因子 --
parent->_bf--;
parent->_right = cur->_right;
}
}
}
else if (cur->_right == nullptr)
{
// 假如删除的是根节点
if (!parent)
{
_root = cur->_left;
delete cur;
return true;
}
else
{
if (parent->_left == cur)
{
// 如果待删除节点在parent的左
// 平衡因子 ++
parent->_bf++;
parent->_left = cur->_left;
}
else
{
// 如果待删除节点在parent的右
// 平衡因子 --
parent->_bf--;
parent->_right = cur->_left;
}
}
}
else
{
// 有两个孩子的情况
// 找右子树的最左节点
Node* rightMinP = cur;
Node* rightMin = cur->_right;
// 循环找最左节点
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
// 交换右子树的最左节点与待删除节点的值
cur->_kv = rightMin->_kv;
// 删除右子树的最左节点
if (rightMin == rightMinP->_left)
{
rightMinP->_bf++;
rightMinP->_left = rightMin->_right;
}
else
{
rightMinP->_bf--;
rightMinP->_right = rightMin->_right;
}
// 重置parent和cur
parent = rightMinP;
cur = rightMin;
}
delete cur;
/
bool is_or_not = false;
while (parent)
{
// 更新平衡因子
if (is_or_not)
{
if (parent->_left == cur)
parent->_bf++;
else
parent->_bf--;
}
is_or_not = true;
if (parent->_bf == 1 || parent->_bf == -1)
return true;
else if (parent->_bf == 0)
{
cur = parent;
parent = parent->_parent;
}
else if(parent->_bf == 2 || parent->_bf == -2)
{
//旋转逻辑
Node* highernode;
int sign;
if (parent->_bf > 0)
{
sign = 1;
highernode = parent->_right;
}
else
{
sign = -1;
highernode = parent->_left;
}
if (highernode->_bf == 0)
{
if (highernode == parent->_right)
{
RotateL(parent);
parent->_bf = 1;
highernode->_bf = -1;
}
else
{
RotateR(parent);
parent->_bf = -1;
highernode->_bf = 1;
}
}
else if (sign == highernode->_bf)
{
// 单旋
if (sign > 0)
RotateL(parent);
else
RotateR(parent);
}
else
{
// 多旋
if (sign > 0)
RotateRL(parent);
else
RotateLR(parent);
}
cur = parent;
parent = parent->_parent;
}
else
{
assert(false);
}
}
}
}
return false;
}
总代码 + 测试代码
总代码
#pragma once
#include<iostream>
using namespace std;
#include<assert.h>
template<class k, class v>
struct AVLTreeNode
{
AVLTreeNode<k, v>* _left;
AVLTreeNode<k, v>* _right;
AVLTreeNode<k, v>* _parent;
int _bf;
pair<k, v> _kv;
AVLTreeNode(const pair<k, v>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
,_bf(0)
,_kv(kv)
{}
};
template<class k, class v>
class AVLTree
{
typedef AVLTreeNode<k, v> Node;
public:
AVLTree() = default;
AVLTree(const AVLTree<k, v>& avl)
{
_root = copy(avl._root);
}
~AVLTree()
{
Destroy(_root);
_root = nullptr;
}
bool Insert(const pair<k, v>& kv)
{
if (!_root)
{
_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 (cur->_kv.first < parent->_kv.first)
parent->_left = cur;
else
parent->_right = 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
{
//左右双旋
RotateLR(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
AVL删除算法
bool Erase(const k& key)
{
if (!_root)return false;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
parent = cur;
cur = cur->_left;
}
else
{
// 删除主逻辑
// 判断三种情况,没孩子,一个孩子,两个孩子
if (cur->_left == nullptr)
{
// 假如删除的是根节点
if (!parent)
{
_root = cur->_right;
delete cur;
return true;
}
else
{
if (parent->_left == cur)
{
// 如果待删除节点在parent的左
// 平衡因子 ++
parent->_bf++;
parent->_left = cur->_right;
}
else
{
// 如果待删除节点在parent的右
// 平衡因子 --
parent->_bf--;
parent->_right = cur->_right;
}
}
}
else if (cur->_right == nullptr)
{
// 假如删除的是根节点
if (!parent)
{
_root = cur->_left;
delete cur;
return true;
}
else
{
if (parent->_left == cur)
{
// 如果待删除节点在parent的左
// 平衡因子 ++
parent->_bf++;
parent->_left = cur->_left;
}
else
{
// 如果待删除节点在parent的右
// 平衡因子 --
parent->_bf--;
parent->_right = cur->_left;
}
}
}
else
{
// 有两个孩子的情况
// 找右子树的最左节点
Node* rightMinP = cur;
Node* rightMin = cur->_right;
// 循环找最左节点
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
// 交换右子树的最左节点与待删除节点的值
cur->_kv = rightMin->_kv;
// 删除右子树的最左节点
if (rightMin == rightMinP->_left)
{
rightMinP->_bf++;
rightMinP->_left = rightMin->_right;
}
else
{
rightMinP->_bf--;
rightMinP->_right = rightMin->_right;
}
// 重置parent和cur
parent = rightMinP;
cur = rightMin;
}
delete cur;
/
bool is_or_not = false;
while (parent)
{
// 更新平衡因子
if (is_or_not)
{
if (parent->_left == cur)
parent->_bf++;
else
parent->_bf--;
}
is_or_not = true;
if (parent->_bf == 1 || parent->_bf == -1)
return true;
else if (parent->_bf == 0)
{
cur = parent;
parent = parent->_parent;
}
else if(parent->_bf == 2 || parent->_bf == -2)
{
//旋转逻辑
Node* highernode;
int sign;
if (parent->_bf > 0)
{
sign = 1;
highernode = parent->_right;
}
else
{
sign = -1;
highernode = parent->_left;
}
if (highernode->_bf == 0)
{
if (highernode == parent->_right)
{
RotateL(parent);
parent->_bf = 1;
highernode->_bf = -1;
}
else
{
RotateR(parent);
parent->_bf = -1;
highernode->_bf = 1;
}
}
else if (sign == highernode->_bf)
{
// 单旋
if (sign > 0)
RotateL(parent);
else
RotateR(parent);
}
else
{
// 多旋
if (sign > 0)
RotateRL(parent);
else
RotateLR(parent);
}
cur = parent;
parent = parent->_parent;
}
else
{
assert(false);
}
}
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
//左单旋
void RotateL(Node* parent)
{
Node* curR = parent->_right;
Node* curRL = curR->_left;
Node* Parentparent = parent->_parent;
//旋转逻辑
parent->_right = curRL;
curR->_left = parent;
//处理parent
if (curRL)curRL->_parent = parent;
parent->_parent = curR;
if (!Parentparent)
{
_root = curR;
curR->_parent = nullptr;
}
else
{
if (Parentparent->_left == parent)
Parentparent->_left = curR;
else
Parentparent->_right = curR;
curR->_parent = Parentparent;
}
curR->_bf = parent->_bf = 0;
}
//右单旋
void RotateR(Node* parent)
{
Node* curL = parent->_left;
Node* curLR = curL->_right;
Node* Parentparent = parent->_parent;
//旋转逻辑
parent->_left = curLR;
curL->_right = parent;
//处理parent
if (curLR)curLR->_parent = parent;
parent->_parent = curL;
if (!Parentparent)
{
_root = curL;
curL->_parent = nullptr;
}
else
{
if (Parentparent->_left == parent)
Parentparent->_left = curL;
else
Parentparent->_right = curL;
curL->_parent = Parentparent;
}
curL->_bf = parent->_bf = 0;
}
//右左双旋
void RotateRL(Node* parent)
{
Node* curR = parent->_right;
Node* curRL = curR->_left;
int bf = curRL->_bf;
RotateR(curR);
RotateL(parent);
if (bf == 0)
{
parent->_bf = 0;
curR->_bf = 0;
curRL->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = -1;
curR->_bf = 0;
curRL->_bf = 0;
}
else if(bf == -1)
{
parent->_bf = 0;
curR->_bf = 1;
curRL->_bf = 0;
}
else
{
assert(false);
}
}
//左右双旋
void RotateLR(Node* parent)
{
Node* curL = parent->_left;
Node* curLR = curL->_right;
int bf = curLR->_bf;
RotateL(curL);
RotateR(parent);
if (bf == 0)
{
parent->_bf = 0;
curL->_bf = 0;
curLR->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = 0;
curL->_bf = -1;
curLR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
curL->_bf = 0;
curLR->_bf = 0;
}
else
{
assert(false);
}
}
int _Height(Node* root)
{
if (!root)return 0;
int left = _Height(root->_left);
int right = _Height(root->_right);
return max(left, right) + 1;
}
void _InOrder(Node* root)
{
if (!root)return;
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
void Destroy(Node* root)
{
if (root == nullptr)return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
Node* copy(Node* root)
{
if (root == nullptr)return nullptr;
Node* newnode = new Node(root->_kv);
newnode->_right = copy(root->_right);
newnode->_left = copy(root->_left);
return newnode;
}
private:
Node* _root;
};
测试代码
#include"AVLTree.h"
#include<string>
void testAVLTree1()
{
int a[] = { 1,2,3,4,5,6 };
AVLTree<int, int> avl;
for (auto e : a)
{
avl.Insert({ e,e });
}
avl.InOrder();
}
void testAVLTree2()
{
AVLTree<string, string> avl;
avl.Insert({ "left","左边"});
avl.Insert({ "right","右边"});
avl.Insert({ "word","单词"});
avl.Insert({ "love","爱你"});
avl.Insert({ "hate","恨你"});
avl.InOrder();
avl.Erase("word");
avl.InOrder();
avl.Erase("left");
avl.InOrder();
avl.Erase("right");
avl.InOrder();
avl.Erase("love");
avl.InOrder();
avl.Erase("hate");
avl.InOrder();
}
int main()
{
//testAVLTree1();
testAVLTree2();
return 0;
}
结语
看到这里,这篇博客有关AVL树的相关内容就讲完啦~( ̄▽ ̄)~*
如果觉得对你有帮助的话,希望可以多多支持博主喔(○` 3′○)