文章目录
- 1. AVL树概念
- 2. AVL树满足性质
- 3. AVL节点结构
- 4. 插入操作
- 5. 检测
- 6. 完整代码
1. AVL树概念
AVL树就是自平衡二叉查找树,为了解决二叉树退化为单链表,使得增删查改时间度为O(N),这里采用控制平衡策略达到效率是O(logN)。
2. AVL树满足性质
- 每个节点的左右子树高度之差绝对不能超过1
- 左边插入(父节点高度差-1)
- 右边插入(父节点高度差+1)
- 不插入(自身节点高度差为0)
3. AVL节点结构
template <class KEY, class VAULE>
struct AVLtree_node
{
AVLtree_node<KEY, VAULE>* _left; //左节点指向
AVLtree_node<KEY, VAULE>* _right; //右节点指向
AVLtree_node<KEY, VAULE>* _parent; //父节点指向
pair<KEY, VAULE> _couple; //存储key/value
int _balanceFactor; //平衡因子(左右子树高度差)
AVLtree_node(const pair<KEY, VAULE>& couple)
:_left(nullptr)
:_right(nullptr)
:_parent(nullptr)
:_couple(couple)
:_balanceFactor(0)
{}
};
4. 插入操作
- 插入阶段
- 根节点为空
- new节点,改变根指向,返回true
- 根节点不为空
- 找到插入位置
- 右查找:当前节点key值 < 插入节点key值
- 左查找:当前节点key值 > 插入节点key值
- 当前节点key值 = 插入节点key值 :直接返回false
- 在对应待插入位置插入
- new节点,当前插入位置指向该节点
- 右插入:当前节点key值 < 插入节点key值
- 左插入: 当前节点key值 > 插入节点key值
- 当前被插入节点父指针指向指向被连接节点
- 自动平衡(TODO)
- 找到插入位置
- 根节点为空
bool insert(const pair<KEY, VAULE>& couple)
{
//二叉搜索树阶段
if (_root == nullptr) //根为空:直接new并指向返回
{
_root = new node(couple);
return true;
}
/*找插入位置*/
node* parent = nullptr; //起初根节点的父节点为nullptr
node* cur = _root; //被插入节点指向
while (cur)
{
if (cur->_couple.first < couple.first) //右查找:当前节点key值 < 插入节点key值
{
parent = cur;
cur = cur->_right;
}
else if (cur->_couple.first > couple.first) //左查找: 当前节点key值 > 插入节点key值
{
parent = cur;
cur = cur->_left;
}
else //当前节点key值 = 插入节点key值:直接退出
{
return false;
}
}
/*在对应位置插入*/
cur = new node(couple);
if (parent->_couple.first > couple.first) //左插入: 当前节点key值 > 插入节点key值
{
parent->_left = newnode;
}
else //右插入:当前节点key值 < 插入节点key值
{
parent->_right = newnode;
}
cur->_parent = parent; //反向链接
/*自动平衡 TODO*/
//AVL树阶段
}
- 平衡阶段铺垫
注意:并没有画上父节点指向,为了便于观察所以没有画,默认每个节点是有父节点指向的
- 右边插入构图
- 左边插入构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rGLjZnpU-1684114665514)(https://typora130.oss-cn-nanjing.aliyuncs.com/QQ截图20230513154519.png)]
上面左右插入构图示范中,观察发现该节点左右子树高度差超过了1就会形成子树单链表,这样就会影响到效率,那么这里的解决办法就是对其旋转处理(待解),另外观察到上面错误高度差情况,会发现一旦插入其被插入节点的部分祖先的平衡因子(高度差)可能改变,所以,插入新增节点会影响部分或全部祖先的平衡因子,平衡因子怎么来更新呢?如果新增节点在其父节点左边,父节点平衡因子-1,如果新增节点在其父节点右边,父节点平衡因子+1,每个节点的平衡因子初始状态都是0。是什么时候平衡因子更新?当插入节点的父节点的平衡因子变化(不为0),那么就需要对其被插入节点的部分或者全部祖先的平衡因子更新。
观察:右边插入三种情况示范:
被插入节点父节点平衡高度不为0,就向上更新祖先的平衡因子,向上更新的依据是该节点左右子树高度变化了;如果插入节点父节点平衡高度为0,就无需向上更新平衡因子
后面两种情况观察:当祖先节点的平衡因子为2或者-2时,就不应该再往上层更新平衡因子了(key值为7的节点的平衡因子就没必要更新了),因为此时只要有一个祖先的平衡因子为2说明就已经有问题了,就需要对这部分子树来调整达到一种平衡状态(旋转处理)。
- 自动平衡
- 更新平衡因子
- 改变被插入节点父节点平衡因子
- 被插入节点在其父节点右子树中:父节点平衡因子+1
- 被插入节点在其父节点左子树中:父节点平衡因子-1
- 判断是否继续向上更新平衡因子
- 父节点平衡因子为1或者-1,继续更新
- 父节点平衡因子为0,无需更新
- 父节点平衡因子为2或者-2,子树不平衡,旋转处理使得平衡(TODO)
- 改变被插入节点父节点平衡因子
- 更新平衡因子
bool insert(const pair<KEY, VAULE>& couple)
{
if (_root == nullptr) //根为空:直接new并指向返回
{
_root = new node(couple);
return true;
}
/*找插入位置*/
node* parent = nullptr; //起初根节点的父节点为nullptr
node* cur = _root; //被插入节点指向
while (cur)
{
if (cur->_couple.first < couple.first) //右查找:当前节点key值 < 插入节点key值
{
parent = cur;
cur = cur->_right;
}
else if (cur->_couple.first > couple.first) //左查找: 当前节点key值 > 插入节点key值
{
parent = cur;
cur = cur->_left;
}
else //当前节点key值 = 插入节点key值:直接退出
{
return false;
}
}
/*在对应位置插入*/
cur = new node(couple);
if (parent->_couple.first > couple.first) //左插入: 当前节点key值 > 插入节点key值
{
parent->_left = newnode;
}
else //右插入:当前节点key值 < 插入节点key值
{
parent->_right = newnode;
}
cur->_parent = parent; //反向链接
/*自动平衡 TODO*/
//更新平衡因子
while (parent)
{
if (cur == parent->_right) //被插入节点在其父节点右子树中
{
parent->_balanceFactor++;
}
else //被插入节点在其父节点左子树中
{
parent->_balanceFactor--;
}
if (parent->_balanceFactor == 1 || parent->_balanceFactor == -1) //继续更新平衡因子
{
parent = parent->_parent;
cur = cur->_parent;
}
else if (parent->_balanceFactor == 0) //无需更新
{
break;
}
else if (parent->_balanceFactor == 2 || parent->_balanceFactor == -2) //子树不平衡(旋转处理)
{
/*TODO*/
break; //旋转之后达到平衡无需更新平衡因子(操作完后便知)
}
else
{
assert(false);
}
}
}
- 平衡阶段旋转处理
旋转目的:让子树平衡并且降低子树高度
- 左单旋
左单旋一定是右高左低
三个举例:
三个举例来抽象得到宏观图:
H为这颗子树高度,当H为0,反应就是上述第一个例子;当H为1,反应就是上述第二个例子;当H为2,反应就是上述第三个例子。当H为x时,其调整方法不变。当H为2时,此时有三种情况的高度为2的树:
但是这里c这棵子树必须是x类型的高度为2的树,a,b这两课子树可以是任意一种都可以。为什么c子树必须是x类型的树呢?如果是y型可能插入不会引发旋转,也可能只会局部旋转:
如何旋转?
- 让cur的左子树(curLeft)放在parent的右子树位置并且curLeft的父节点指向指向parent
- 让parent放在cur的左子树位置并且parent的父节点指向指向cur
- cur变为子树或者整颗树的根
- 更新parent和cur的平衡因子
//parent->_balanceFactor == 2 && cur->_balanceFactor = 1
void leftSingleRotate(node* parent) //左单旋
{
//记录指针
node* parent_RightChild = parent->_right; //parent_RightChild = cur
node* parent_RightChild_LeftChild = parentRightChild->_left; //parent_RightChild_LeftChild = curLeft
node* parent_parent = parent->_parent; //局部根或整棵树根的父节点
parent->_right = parent_RightChild_LeftChild; //让cur的左子树(curLeft)放在parent的右子树位置
if (parent_RightChild_LeftChild != nullptr) //H为0时,parent_RightChild_LeftChild=nullptr
{
parent_RightChild_LeftChild->_parent = parent; //curLeft的父节点指向指向parent
}
parent_RightChild->_left = parent; //让parent放在cur的左子树位置
parent->_parent = parent_RightChild; //parent的父节点指向指向cur
//cur(parent_RightChild)变为子树或者整颗树的根
if (parent_parent == nullptr) //parent是整颗树的根
{
_root = parent_RightChild;
_root->_parent = nullptr;
}
else //parent是局部子树的根
{
if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
{
parent_parent->_left = parent_RightChild;
}
else //parent节点在父节点的右子树位置
{
parent_parent->_right = parent_RightChild;
}
parent_RightChild->_parent = parent_parent;
}
//更新平衡因子
parent->_balanceFactor = parent_RightChild->_balanceFactor = 0;
}
- 右单旋
右单旋一定是左高右低
三个举例来抽象得到宏观图:
H为这颗子树高度,当H为0,反应就是上述第一个例子;当H为1,反应就是上述第二个例子;当H为2,反应就是上述第三个例子。当H为x时,其调整方法不变。当H为2时,这里的c子树必须是x类型的树,此时有三种情况的高度为2的树:
如何旋转?
- 让cur的右子树(curRight)放在parent的左子树位置并且让curRight父节点的指向指向parent
- 让parent放在cur的右子树位置并且让parent的父节点指向指向cur
- cur变为子树或者整颗树的根
- 更新parent和cur的平衡因子
//parent->_balanceFactor == -2 && cur->_balanceFactor == -1
void rightSingleRotate(node* parent) //右单旋
{
//记录指针
node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
node* parent_parent = parent->_parent; //局部根或整棵树根的父节点
parent->_left = parent_LeftChild_RightChild; //让cur的右子树(curRight)放在parent的左子树位置
if (parent_LeftChild_RightChild != nullptr)
{
parent_LeftChild_RightChild->_parent = parent; //让curRight父节点的指向指向parent
}
parent_LeftChild->_right = parent; //让parent放在cur的右子树位置
parent->_parent = parent_LeftChild; //让parent的父节点指向指向cur
//cur(parent_LeftChild)变为子树或者整颗树的根
if (parent_parent == nullptr) //parent是整颗树的根
{
_root = parent_LeftChild; //cur(parent_LeftChild)就是根
_root->_parent = nullptr;
}
else //parent是局部子树的根
{
if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
{
parent_parent->_left = parent_LeftChild;
}
else //parent节点在父节点的右子树位置
{
parent_parent->_right = parent_LeftChild;
}
parent_LeftChild->_parent = parent_parent; //cur(parent_LeftChild)指向局部根的父节点
}
//更新平衡因子
parent->_balanceFactor = parent_LeftChild->_balanceFactor = 0;
}
- 先左后右旋
抽象图:
H为子树高度,H为0时,此时60这个节点也不存在,新增节点必须是key为60这个节点位置;H为1,2,3…等等图形这里不再画出,都可以通过抽象达成相同调整方法,这里被插入节点一定是b,c子树位置或者b,c的子树孩子位置。
如何旋转?
- 先左旋cur为根的子树
- 让cur的右指向指向curRightLeft并且curRightLeft的父节点的指向指向cur
- 让curRight的左指向指向cur并且cur的父节点的指向指向curRight
- curRight作为子树根并且改变curRight的父节点的指向指向parent
- 更新cur和curRight的平衡因子为0
- 后右旋parent为根的子树
- 让parent的左指向指向curRightRight并且curRightRight的父节点的指向指向parent
- 让curRight的右子树指向parent并且parent的父节点的指向指向curRight
- curRight作为树的根并且改变curRight的父节点指向指向空
- 更新parent和curRight的平衡因子为0
- 更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
- c子树插入(curRight->_balanceFactor=1):最终cur的平衡因子为-1,parent平衡因子为0,curRight平衡因子为0
- b子树插入(curRight->_balanceFactor=-1):最终cur的平衡因子为0,parent平衡因子为1,curRight平衡因子为0
- curRight本身是被插入节点(curRight->_balanceFactor=0,H=0),最终cur、parent、curRight平衡因子都为0
//parent->_balanceFactor == -2 && cur->_balanceFactor == 1
void leftRightRotate(node* parent)
{
node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
int balanceFactor = parent_LeftChild_RightChild->_balanceFactor; //记录curRight的平衡因子值:确定在b子树还是c子树插入
leftSingleRotate(parent->_left); //左单旋cur为根树
rightSingleRotate(parent); //右单旋parent为根的树
//更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
if (balanceFactor == 1) //c子树插入
{
parent->_balanceFactor = 0;
parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
parent_LeftChild = -1; //cur->_balanceFactor = -1
}
else if (balanceFactor == -1) //b子树插入
{
parent->_balanceFactor = 1;
parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
parent_LeftChild = 0; //cur->_balanceFactor = 0
}
else if (balanceFactor == 0) //curRight自身是被插入节点
{
parent->_balanceFactor = 0;
parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
parent_LeftChild = 0; //cur->_balanceFactor = 0
}
else
{
assert(false);
}
}
- 先右后左旋
抽象图:
H为子树高度,H为0时,此时60这个节点也不存在,新增节点必须是key为60这个节点位置;H为1,2,3…等等图形这里不再画出,都可以通过抽象达成相同调整方法,这里被插入节点一定是b,c子树位置或者b,c的子树孩子位置。
如何旋转?
- 先右旋cur为根的子树
- 让cur的左指向指向curLeftRight并且curLeftRight的父节点的指向指向cur
- 让curLeft的右指向指向cur并且cur的父节点的指向指向curLeft
- curLeft作为子树的根并且curLeft的父节点的指向指向parent
- 更新cur和curLeft的平衡因子为0
- 后左旋parent为根的树
- 让parent的右指向指向curLeftLeft并且curLeftLeft的父节点的指向指向parent
- 让curLeft的左指向指向parent并且paren的父节点的指向指向curLeft
- curLeft作为树的根并且改变curLeft的父节点的指向指向nullptr
- 更新parent和curLeft的平衡因子为0
- 更新cur、curLeft、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
- b子树插入(curLeft->_balance=1),最终cur的平衡因子为0,parent平衡因子为-1,curLeft平衡因子为0
- c子树插入(curLeft->_balance=-1),最终cur的平衡因子为1,parent平衡因子为0,curLeft平衡因子为0
- curLeft本身是被插入节点(curLeft->_balanceFactor=0,H=0),最终cur、parent、curRight平衡因子都为0
//parent->_balanceFactor == 2 && cur->_balanceFactor == -1
void rightLeftRotate(node* parent) //先右后左旋
{
node* parent_RightChild = parent->_right; //parent_RightChild = cur
node* parent_RightChild_LeftChild = parent_RightChild->_left; //parent_RightChild_LeftChild = curLeft
int balanceFactor = parent_RightChild_LeftChild->_balanceFactor; 记录curLeft的平衡因子值:确定在b子树还是c子树插入
rightSingleRotate(parent_RightChild); //右单旋cur为根的树
leftSingleRotate(parent); //左单旋parent为根的树
//更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
if (balanceFactor == 1) //b子树插入
{
parent_RightChild->_balanceFactor = 0; //cur->_balanceFactor = 0
parent->_balanceFactor = -1;
parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
}
else if (balanceFactor == -1) //c子树插入
{
parent_RightChild->_balanceFactor = 1; //cur->_balanceFactor = 1
parent->_balanceFactor = 0;
parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
}
else if (balanceFactor == 0) //curLeft本身是被插入节点
{
parent_RightChild->_balanceFactor = 0; //cur->_balanceFactor = 0
parent->_balanceFactor = 0;
parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
}
else
{
assert(false);
}
}
5. 检测
- 中序遍历是否升序
void inOrder() //中序遍历
{
_inOrder(_root);
cout << endl;
}
void _inOrder(node* root) //中序遍历
{
if (root == nullptr)
{
return;
}
_inOrder(root->_left);
cout << root->_couple.first << " ";
_inOrder(root->_right);
}
- 是否平衡
bool isBalance() //判断树是否为平衡树
{
return _isBalance(_root);
}
int heightDiffer(node* root)
{
if (root == nullptr)
return 0;
int leftHeight = heightDiffer(root->_left);
int rightHeight = heightDiffer(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _isBalance(node* root) //判断树是否为平衡树(检查每个节点的左右子树高度差)
{
if (root == nullptr)
return true;
int leftHeight = heightDiffer(root->_left);
int rightHeight = heightDiffer(root->_right);
if (rightHeight - leftHeight != root->_balanceFactor) //不只是对高度检查还应该对平衡因子检查
{
cout << "key:" << root->_couple.first << " node balanceFactor exception!" << endl;
return false;
}
return abs(leftHeight - rightHeight) < 2
&& _isBalance(root->_left)
&& _isBalance(root->_right);
}
6. 完整代码
插入阶段
- 根节点为空
- new节点,改变根指向,返回true
- 根节点不为空
- 找到插入位置
- 右查找:当前节点key值 < 插入节点key值
- 左查找:当前节点key值 > 插入节点key值
- 当前节点key值 = 插入节点key值 :直接返回false
- 在对应待插入位置插入
- new节点,当前插入位置指向该节点
- 右插入:当前节点key值 < 插入节点key值
- 左插入: 当前节点key值 > 插入节点key值
- 当前被插入节点父指针指向指向被连接节点
- 自动平衡
- 更新平衡因子
- 改变被插入节点父节点平衡因子
- 被插入节点在其父节点右子树中:父节点平衡因子+1
- 被插入节点在其父节点左子树中:父节点平衡因子-1
- 判断是否继续向上更新平衡因子
- 父节点平衡因子为1或者-1,继续更新
- 父节点平衡因子为0,无需更新
- 父节点平衡因子为2或者-2,子树不平衡,旋转处理使得平衡(旋转处理)
- parent->_balanceFactor == 2 && cur->_balanceFactor == 1 --> 左单旋
- parent->_balanceFactor == -2 && cur->_balanceFactor == -1 --> 右单旋
- parent->_balanceFactor == -2 && cur->_balanceFactor == 1 --> 先左后右旋
- parent->_balanceFactor == 2 && cur->_balanceFactor == -1 --> 先右后左旋
- 改变被插入节点父节点平衡因子
- 更新平衡因子
- 找到插入位置
#pragma once
#include <iostream>
#include <utility>
#include <cassert>
using namespace std;
template <class KEY, class VAULE>
struct AVLtree_node
{
AVLtree_node<KEY, VAULE>* _left; //左节点指向
AVLtree_node<KEY, VAULE>* _right; //右节点指向
AVLtree_node<KEY, VAULE>* _parent; //父节点指向
pair<KEY, VAULE> _couple; //存储key/value
int _balanceFactor; //平衡因子(左右子树高度差)
AVLtree_node(const pair<KEY, VAULE>& couple)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_couple(couple)
,_balanceFactor(0)
{}
};
template <class KEY, class VAULE>
class AVLtree
{
typedef AVLtree_node<KEY, VAULE> node;
public:
bool insert(const pair<KEY, VAULE>& couple)
{
if (_root == nullptr) //根为空:直接new并指向返回
{
_root = new node(couple);
return true;
}
/*找插入位置*/
node* parent = nullptr; //起初根节点的父节点为nullptr
node* cur = _root; //被插入节点指向
while (cur)
{
if (cur->_couple.first < couple.first) //右查找:当前节点key值 < 插入节点key值
{
parent = cur;
cur = cur->_right;
}
else if (cur->_couple.first > couple.first) //左查找: 当前节点key值 > 插入节点key值
{
parent = cur;
cur = cur->_left;
}
else //当前节点key值 = 插入节点key值:直接退出
{
return false;
}
}
/*在对应位置插入*/
cur = new node(couple);
if (parent->_couple.first > couple.first) //左插入: 当前节点key值 > 插入节点key值
{
parent->_left = cur;
}
else //右插入:当前节点key值 < 插入节点key值
{
parent->_right = cur;
}
cur->_parent = parent; //反向链接
/*自动平衡 TODO*/
//更新平衡因子
while (parent)
{
if (cur == parent->_right) //被插入节点在其父节点右子树中
{
parent->_balanceFactor++;
}
else //被插入节点在其父节点左子树中
{
parent->_balanceFactor--;
}
if (parent->_balanceFactor == 1 || parent->_balanceFactor == -1) //继续更新平衡因子
{
parent = parent->_parent;
cur = cur->_parent;
}
else if (parent->_balanceFactor == 0)
{
break;
}
else if (parent->_balanceFactor == 2 || parent->_balanceFactor == -2) //子树不平衡(旋转处理)
{
if (parent->_balanceFactor == 2 && cur->_balanceFactor == 1) //左单旋
{
leftSingleRotate(parent);
}
else if (parent->_balanceFactor == -2 && cur->_balanceFactor == -1) //右单旋
{
rightSingleRotate(parent);
}
else if (parent->_balanceFactor == -2 && cur->_balanceFactor == 1) //先左后右旋
{
leftRightRotate(parent);
}
else if (parent->_balanceFactor == 2 && cur->_balanceFactor == -1) //先右后左旋
{
rightLeftRotate(parent);
}
else
{
assert(false);
}
//旋转可以达到左右子树平衡并且降低高度
break;
}
else
{
assert(false);
}
}
return true;
}
void inOrder() //中序遍历
{
_inOrder(_root);
cout << endl;
}
bool isBalance() //判断树是否为平衡树
{
return _isBalance(_root);
}
private:
void leftSingleRotate(node* parent) //左单旋
{
//记录指针
node* parent_RightChild = parent->_right; //parent_RightChild = cur
node* parent_RightChild_LeftChild = parent_RightChild->_left; //parent_RightChild_LeftChild = curLeft
node* parent_parent = parent->_parent; //局部根或整棵树根的父节点
parent->_right = parent_RightChild_LeftChild; //让cur的左子树(curLeft)放在parent的右子树位置
if (parent_RightChild_LeftChild != nullptr) //H为0时,parent_RightChild_LeftChild=nullptr
{
parent_RightChild_LeftChild->_parent = parent; //curLeft的父节点指向指向parent
}
parent_RightChild->_left = parent; //让parent放在cur的左子树位置
parent->_parent = parent_RightChild; //parent的父节点指向指向cur
//cur(parent_RightChild)变为子树或者整颗树的根
if (parent_parent == nullptr) //parent是整颗树的根
{
_root = parent_RightChild; //cur(parent_RightChild)就是根
_root->_parent = nullptr;
}
else //parent是局部子树的根
{
if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
{
parent_parent->_left = parent_RightChild;
}
else //parent节点在父节点的右子树位置
{
parent_parent->_right = parent_RightChild;
}
parent_RightChild->_parent = parent_parent; //cur(parent_RightChild)指向局部根的父节点
}
//更新平衡因子
parent->_balanceFactor = parent_RightChild->_balanceFactor = 0;
}
void rightSingleRotate(node* parent) //右单旋
{
//记录指针
node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
node* parent_parent = parent->_parent; //局部根或整棵树根的父节点
parent->_left = parent_LeftChild_RightChild; //让cur的右子树(curRight)放在parent的左子树位置
if (parent_LeftChild_RightChild != nullptr)
{
parent_LeftChild_RightChild->_parent = parent; //让curRight父节点的指向指向parent
}
parent_LeftChild->_right = parent; //让parent放在cur的右子树位置
parent->_parent = parent_LeftChild; //让parent的父节点指向指向cur
//cur(parent_LeftChild)变为子树或者整颗树的根
if (parent_parent == nullptr) //parent是整颗树的根
{
_root = parent_LeftChild; //cur(parent_LeftChild)就是根
_root->_parent = nullptr;
}
else //parent是局部子树的根
{
if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
{
parent_parent->_left = parent_LeftChild;
}
else //parent节点在父节点的右子树位置
{
parent_parent->_right = parent_LeftChild;
}
parent_LeftChild->_parent = parent_parent; //cur(parent_LeftChild)指向局部根的父节点
}
//更新平衡因子
parent->_balanceFactor = parent_LeftChild->_balanceFactor = 0;
}
void leftRightRotate(node* parent) //先左后右旋
{
node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
int balanceFactor = parent_LeftChild_RightChild->_balanceFactor; //记录curRight的平衡因子值:确定在b子树还是c子树插入
leftSingleRotate(parent->_left); //左单旋cur为根的树
rightSingleRotate(parent); //右单旋parent为根的树
//更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
if (balanceFactor == 1) //c子树插入
{
parent->_balanceFactor = 0;
parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
parent_LeftChild->_balanceFactor = -1; //cur->_balanceFactor = -1
}
else if (balanceFactor == -1) //b子树插入
{
parent->_balanceFactor = 1;
parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
parent_LeftChild->_balanceFactor = 0; //cur->_balanceFactor = 0
}
else if (balanceFactor == 0) //curRight自身是被插入节点
{
parent->_balanceFactor = 0;
parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
parent_LeftChild->_balanceFactor = 0; //cur->_balanceFactor = 0
}
else
{
assert(false);
}
}
void rightLeftRotate(node* parent) //先右后左旋
{
node* parent_RightChild = parent->_right; //parent_RightChild = cur
node* parent_RightChild_LeftChild = parent_RightChild->_left; //parent_RightChild_LeftChild = curLeft
int balanceFactor = parent_RightChild_LeftChild->_balanceFactor; 记录curLeft的平衡因子值:确定在b子树还是c子树插入
rightSingleRotate(parent_RightChild); //右单旋cur为根的树
leftSingleRotate(parent); //左单旋parent为根的树
//更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
if (balanceFactor == 1) //b子树插入
{
parent_RightChild->_balanceFactor = 0; //cur->_balanceFactor = 0
parent->_balanceFactor = -1;
parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
}
else if (balanceFactor == -1) //c子树插入
{
parent_RightChild->_balanceFactor = 1; //cur->_balanceFactor = 1
parent->_balanceFactor = 0;
parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
}
else if (balanceFactor == 0) //curLeft本身是被插入节点
{
parent_RightChild->_balanceFactor = 0; //cur->_balanceFactor = 0
parent->_balanceFactor = 0;
parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
}
else
{
assert(false);
}
}
void _inOrder(node* root) //中序遍历
{
if (root == nullptr)
{
return;
}
_inOrder(root->_left);
cout << root->_couple.first << " ";
_inOrder(root->_right);
}
int heightDiffer(node* root)
{
if (root == nullptr)
return 0;
int leftHeight = heightDiffer(root->_left);
int rightHeight = heightDiffer(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _isBalance(node* root) //判断树是否为平衡树(检查每个节点的左右子树高度差)
{
if (root == nullptr)
return true;
int leftHeight = heightDiffer(root->_left);
int rightHeight = heightDiffer(root->_right);
if (rightHeight - leftHeight != root->_balanceFactor) //不只是对高度检查还应该对平衡因子检查
{
cout << "key:" << root->_couple.first << " node balanceFactor exception!" << endl;
return false;
}
return abs(leftHeight - rightHeight) < 2
&& _isBalance(root->_left)
&& _isBalance(root->_right);
}
private:
node* _root = nullptr;
};
/*测试*/
void test_AVLtree1()
{
//int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14};
AVLtree<int, int> tree;
for (auto x : arr)
{
tree.insert(make_pair(x, x));
}
tree.inOrder(); //中序遍历
cout << tree.isBalance() << endl; //判断是否为平衡树
}
void test_perfermance() //测试性能
{
srand(time(nullptr));
const size_t N = 100000;
AVLtree<int, int> tree;
for (size_t i = 0; i < N; ++i)
{
size_t x = rand();
tree.insert(make_pair(x, x));
}
cout << tree.isBalance() << endl;
}