插入操作
// 插入操作
bool insert(const pair<K, V>& kv)
{
// 若树为空,直接构造,new一个
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
// 用于遍历树的当前节点
Node* cur = _root;
// 用于记录当前节点的父节点
Node* parent = nullptr;
// 循环查找插入位置
while (cur)
{
// 插入值大于当前节点值,向右移动
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_kv.first;
}
// 插入值小于当前节点值,向左移动
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
// 插入值等于当前节点值,去重
else
{
return false;
}
}
// 跳出循环后,cur指向空,创建新节点
cur = new Node(kv);
// 根据父节点的值决定新节点是父节点的左子节点还是右子节点
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
// 设置新节点的父节点
cur->_parent = parent;
// 控制平衡
while (parent)
{
// 右加左减
if (cur == parent->_left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
// 平衡因子为0,说明子树平衡,跳出循环
if (parent->_bf == 0)
{
break;
}
// 平衡因子为1或-1,说明子树仍平衡,继续向上更新
else if (parent->_bf == 1 || parent->_bf == -1)
{
// 继续更新
cur = parent;
parent = parent->_parent;
}
// 平衡因子为2或-2,说明子树不平衡,需要旋转
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);
}
}
else
{
assert(false);
}
}
return true;
}
1. 函数功能概述:这个函数的主要功能是向树中插入一个新的键值对 kv ,并且在插入后通过旋转操作来维护树的平衡。
2. 树为空的情况:
- if (_root == nullptr) :检查树是否为空。如果为空,创建一个新的节点并将其设置为根节点,然后返回 true ,表示插入成功。
3. 查找插入位置:
- 使用 while (cur) 循环来遍历树,根据当前节点的值与插入值的大小关系,决定是向左还是向右移动。
- 如果插入值等于当前节点的值,返回 false ,表示插入失败(去重操作)。
4. 插入新节点:
- 循环结束后, cur 指向空,创建一个新的节点并将其插入到合适的位置(作为父节点的左子节点或右子节点),并设置新节点的父节点。
5. 维护树的平衡:
- 使用 while (parent) 循环向上更新节点的平衡因子。
- 根据新节点是父节点的左子节点还是右子节点,更新父节点的平衡因子。
- 如果平衡因子为0,说明子树平衡,跳出循环。
- 如果平衡因子为1或-1,继续向上更新。
- 如果平衡因子为2或-2,根据节点的平衡因子决定进行哪种旋转操作(左旋 RotateL 、右旋 RotateR 、先左后右双旋 RotateRL 、先右后左双旋 RotateLR )。
6. 返回值:最后返回 true ,表示插入操作成功。
这段代码主要实现了在平衡二叉树(如AVL树)中插入节点并维护树的平衡的功能。不过,代码中存在一些小错误,例如 cur = cur->_kv.first 这一行应该是 cur = cur->_right ,可能是笔误。同时,代码中使用的 RotateL 、 RotateR 、 RotateRL 和 RotateLR 函数的具体实现也需要根据实际情况进行补充。

//左转
void RotateL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
Node* ppnode = parent->_parent;
parent->_parent = cur;
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_parent= cur;
}
cur->_parent = ppnode;
}
parent->_bf = cur->_bf = 0;
}
以下是对这段代码 RotateL (左旋操作)的详细解析:
1. 函数声明:
void RotateL(Node* parent)
这是一个名为 RotateL 的函数,它接受一个指向 Node 类型的指针 parent 作为参数,函数的返回值类型为 void ,即不返回任何值。 parent 表示要进行左旋操作的节点。
1. 变量定义:
Node* cur = parent->_right; Node* curleft = cur->_left;
定义了两个指针变量 cur 和 curleft 。 cur 指向 parent 的右子节点, curleft 指向 cur 的左子节点。这里的操作是为左旋操作做准备,获取相关节点的指针以便后续调整指针指向。
1. 调整 parent 的右子节点:
parent->_right = curleft; if (curleft) { curleft->_parent = parent; }
将 parent 的右子节点设置为 curleft 。如果 curleft 不为空(即 cur 原本有左子节点),则将 curleft 的父节点设置为 parent 。这一步是为了重新连接 parent 和 curleft 之间的父子关系。
1. 调整 cur 的左子节点:
cur->_left = parent;
将 cur 的左子节点设置为 parent ,这是左旋操作中改变指针指向的关键步骤之一,使得 parent 成为 cur 的左子节点。
1. 处理 parent 的父节点:
Node* ppnode = parent->_parent; parent->_parent = cur;
定义了一个指针 ppnode 指向 parent 的父节点。然后将 parent 的父节点设置为 cur ,这一步是为了更新 parent 的父节点信息,使其指向 cur 。
1. 处理根节点情况:
if (parent == _root) { _root = cur; cur->_parent = nullptr; }
如果 parent 是根节点(即 parent 是整个树的根),则将根节点 _root 指向 cur ,并将 cur 的父节点设置为 nullptr ,这是因为 cur 成为了新的根节点,它没有父节点。
1. 处理非根节点情况:
else { if (ppnode->_left == parent) { ppnode->_left = cur; } else { ppnode->_parent= cur; } cur->_parent = ppnode; }
如果 parent 不是根节点,则根据 parent 是 ppnode 的左子节点还是右子节点,相应地将 ppnode 的左子节点或右子节点设置为 cur 。最后将 cur 的父节点设置为 ppnode ,确保 cur 与 ppnode 之间的父子关系正确。
1. 设置平衡因子:
parent->_bf = cur->_bf = 0;
将 parent 和 cur 的平衡因子 _bf 都设置为 0,这是在左旋操作完成后对平衡因子的更新,因为左旋操作可能会影响节点的平衡状态,这里简单地将它们的平衡因子设为 0,具体的平衡因子计算可能需要根据树的其他信息进一步处理。

//右转
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left= curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right= parent;
Node* ppnode = parent->_parent;
cur->_right = parent;
if (ppnode==nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right= cur;
}
cur->_parent = ppnode;
}
parent->_bf = cur->_bf = 0;
}
以下是对这段 RotateR (右转操作)代码的详细解析:
1. 函数声明:
void RotateR(Node* parent)
这是一个名为 RotateR 的函数,接收一个指向 Node 类型的指针 parent 作为参数,函数返回值类型为 void ,即不返回任何值。 parent 表示要进行右转操作的节点。
1. 变量定义:
Node* cur = parent->_left; Node* curright = cur->_right;
定义了两个指针变量 cur 和 curright 。 cur 指向 parent 的左子节点, curright 指向 cur 的右子节点。这是为右转操作做准备,获取相关节点的指针以便后续调整指针指向。
1. 调整 parent 的左子节点:
parent->_left = curright; if (curright) { curright->_parent = parent; }
将 parent 的左子节点设置为 curright 。如果 curright 不为空(即 cur 原本有右子节点),则将 curright 的父节点设置为 parent 。这一步是为了重新连接 parent 和 curright 之间的父子关系。
1. 调整 cur 的右子节点:
cur->_right = parent;
将 cur 的右子节点设置为 parent ,这是右转操作中改变指针指向的关键步骤之一,使得 parent 成为 cur 的右子节点。
1. 处理 parent 的父节点:
Node* ppnode = parent->_parent;
定义了一个指针 ppnode 指向 parent 的父节点。
1. 处理根节点情况:
if (ppnode == nullptr) { _root = cur; cur->_parent = nullptr; }
如果 ppnode 为空(即 parent 原本是根节点),则将根节点 _root 指向 cur ,并将 cur 的父节点设置为 nullptr ,因为 cur 成为了新的根节点,它没有父节点。
1. 处理非根节点情况:
else { if (ppnode->_left == parent) { ppnode->_left = cur; } else { ppnode->_right = cur; } cur->_parent = ppnode; }
如果 ppnode 不为空(即 parent 不是根节点),根据 parent 是 ppnode 的左子节点还是右子节点,相应地将 ppnode 的左子节点或右子节点设置为 cur 。最后将 cur 的父节点设置为 ppnode ,确保 cur 与 ppnode 之间的父子关系正确。
1. 设置平衡因子:
parent->_bf = cur->_bf = 0;
将 parent 和 cur 的平衡因子 _bf 都设置为 0,这是在右转操作完成后对平衡因子的更新,因为右转操作可能会影响节点的平衡状态,这里简单地将它们的平衡因子设为 0,具体的平衡因子计算可能需要根据树的其他信息进一步处理。



//右左双旋
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 0)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
cur->_bf = 1;
curleft->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
以下是对 RotateRL 函数(右左双旋操作)的详细解析:
1. 函数声明:
void RotateRL(Node* parent)
这是一个名为 RotateRL 的函数,接受一个指向 Node 类型的指针 parent 作为参数,函数的返回值类型为 void ,即不返回任何值。 parent 表示要进行右左双旋操作的节点。
1. 变量定义:
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;
定义了三个变量。 cur 指向 parent 的右子节点, curleft 指向 cur 的左子节点, bf 用于存储 curleft 的平衡因子 _bf 。这些操作是为后续的双旋操作获取相关节点的信息和平衡因子。
1. 第一次旋转(右旋):
RotateR(parent->_right);
调用 RotateR 函数(右旋函数),对 parent 的右子节点进行右旋操作。右旋操作会调整树的结构,改变节点之间的指针指向。
1. 第二次旋转(左旋):
RotateL(parent);
调用 RotateL 函数(左旋函数),对 parent 进行左旋操作。左旋操作同样会调整树的结构,改变节点之间的指针指向。经过先右旋再左旋的操作,实现了右左双旋,对树的整体结构进行了调整。
1. 更新平衡因子:
if (bf == 0) { cur->_bf = 0; curleft->_bf = 0; parent->_bf = 0; } else if (bf == 1) { cur->_bf = 0; curleft->_bf = 0; parent->_bf = -1; } else if (bf == -1) { cur->_bf = 1; curleft->_bf = 0; parent->_bf = 0; } else { assert(false); }
根据之前保存的 curleft 的平衡因子 bf 的值,来更新 cur 、 curleft 和 parent 的平衡因子。
- 当 bf 为 0 时,将 cur 、 curleft 和 parent 的平衡因子都设为 0 ,表示这些节点在双旋操作后达到了平衡状态。
- 当 bf 为 1 时,将 cur 和 curleft 的平衡因子设为 0 ,将 parent 的平衡因子设为 -1 ,更新它们的平衡因子以反映双旋后的状态。
- 当 bf 为 -1 时,将 cur 的平衡因子设为 1 , curleft 的平衡因子设为 0 , parent 的平衡因子设为 0 ,调整它们的平衡因子。
- 如果 bf 不是上述的 0 、 1 、 -1 这几种情况,通过 assert(false) 来触发断言,通常意味着出现了异常情况,因为正常情况下平衡因子应该是这几个值之一。

//左右双旋
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
int bf = curright->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 0)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
cur->_bf = -1;
curleft->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = 1;
}
else
{
assert(false);
}
}
以下是对 RotateLR 函数(左右双旋操作)的详细解析:
1. 函数声明:
void RotateLR(Node* parent)
该函数名为 RotateLR ,接受一个指向 Node 类型的指针 parent 作为参数,返回值类型为 void ,即不返回任何值。 parent 是要进行左右双旋操作的节点。
1. 变量定义:
Node* cur = parent->_left; Node* curright = cur->_right; int bf = curright->_bf;
定义了三个变量。 cur 指向 parent 的左子节点; curright 指向 cur 的右子节点; bf 用于存储 curright 的平衡因子 _bf 。这些操作是为后续的双旋操作获取相关节点的信息和平衡因子。
1. 第一次旋转(左旋):
RotateL(parent->_left);
调用 RotateL 函数(左旋函数),对 parent 的左子节点进行左旋操作。左旋操作会改变树的结构,调整节点之间的指针指向。
1. 第二次旋转(右旋):
RotateR(parent);
调用 RotateR 函数(右旋函数),对 parent 进行右旋操作。右旋操作同样会调整树的结构,改变节点之间的指针指向。先左旋再右旋的操作,实现了左右双旋,对树的整体结构进行调整。
1. 更新平衡因子:
if (bf == 0) { cur->_bf = 0; // 这里原代码应该是curright->_bf = 0,可能是笔误,按逻辑应该是这个节点的平衡因子设为0 curright->_bf = 0; parent->_bf = 0; } else if (bf == 1) { cur->_bf = -1; curright->_bf = 0; parent->_bf = 0; } else if (bf == -1) { cur->_bf = 0; curright->_bf = 0; parent->_bf = 1; } else { assert(false); }
根据之前保存的 curright 的平衡因子 bf 的值,来更新 cur 、 curright 和 parent 的平衡因子:
- 当 bf 为 0 时,将 cur 、 curright 和 parent 的平衡因子都设为 0 ,表示这些节点在双旋操作后达到了平衡状态。
- 当 bf 为 1 时,将 cur 的平衡因子设为 -1 , curright 的平衡因子设为 0 , parent 的平衡因子设为 0 ,更新它们的平衡因子以反映双旋后的状态。
- 当 bf 为 -1 时,将 cur 的平衡因子设为 0 , curright 的平衡因子设为 0 , parent 的平衡因子设为 1 ,调整它们的平衡因子。
- 如果 bf 不是上述的 0 、 1 、 -1 这几种情况,通过 assert(false) 来触发断言,意味着出现了异常情况,因为正常情况下平衡因子应该是这几个值之一。
完整代码
#include<iostream>
#include<assert.h>
using namespace std;
template<class K,class V>
struct AVLTreeNode
{
pair<K, V>_kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
{}
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V>Node;
public:
//插入操作
bool insert(const pair<K, V>& kv)
{
if (_root == nullptr) //若树为空,直接构造,new一个
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_kv .first< kv.first)//插入值大于root右移
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first> kv.first)//插入值小于root左移
{
parent = cur;
cur = cur->_left;
}
else //相等去重
{
return false;
}
}
//跳空判断cur的位置
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
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);
}
}
else
{
assert(false);
}
}
return true;
}
//左转
void RotateL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
Node* ppnode = parent->_parent;
parent->_parent = cur;
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_parent= cur;
}
cur->_parent = ppnode;
}
parent->_bf = cur->_bf = 0;
}
//右转
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left= curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right= parent;
Node* ppnode = parent->_parent;
cur->_right = parent;
if (ppnode==nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right= cur;
}
cur->_parent = ppnode;
}
parent->_bf = cur->_bf = 0;
}
//右左双旋
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 0)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
cur->_bf = 1;
curleft->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
//左右双旋
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
int bf = curright->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 0)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
cur->_bf = -1;
curleft->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = 1;
}
else
{
assert(false);
}
}
private:
Node* _root=nullptr;
};
1197

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



