当前位置: 首页 > news >正文

【C++】AVL树

一、AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查
找元素相当于在顺序表中搜索元素,效率低下。从原来的时间复杂度O(logn)转变为O(n)

因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/1/0)

二、AVL树节点的定义

AVL的概念:左右子树高度差不超过1

因此要在二叉搜索树上增加一个平衡因子(来记录左右子树的高度差),当平衡因子超过1时,就要更新向上调整父节点的平衡因子(因为子节点的平衡因子发生变化,间接的导致父节点的平衡因子发生改变),这时就要知道父节点的位置,因此在二叉搜索树上要多维护一个父节点的指针。

struct AVLTreeNode
{pair<K, V> _key;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf; // balance fatcorAVLTreeNode(const pair<K, V>& key): _key(key), _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0){}
};

三、AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子
1. 按照二叉搜索树的方式插入新节点
2. 调整节点的平衡因子

下面是二叉搜索树的插入,这个树的结构是左子树小于根,右子树大于根。 

	bool Insert(const pair<K, V>& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key.first < key.first){parent = cur;cur = cur->_right;}else if (cur->_key.first > key.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key.first < key.first){parent->_right = cur;}else {parent->_left = cur;}
}

调整节点的平衡因子

插入新节点,会影响部分祖先节点的平衡因子,因此要不断的更新平衡因子。

插入在左子树,parent平衡因子--

插入在右子树,parent平衡因子++

1、parent的平衡因子 == 0

parent的平衡因子更新前是 1 or -1,插入节点插入在矮的那边。parent所在子树的高度不变,不需要继续往上更新

2、parent的平衡因子 == 1 / -1

说明parent的平衡因子更新前是0,插入节点插入在任意一边。parent所在的子树高度都会变化了,需要继续往上更新

3、parent的平衡因子 == 2 / -2

说明parent的平衡因子更新前是1 / -1,插入节点插入在高的那边,进一步加剧了parent所在子树的不平衡,所以要发生旋转,旋转后要更新平衡因子

		// 因为因为父节点要连接一下父节点的位置cur->_parent = parent;// 更新平衡因子while (parent){if (cur == parent->_left)parent->_bf--;elseparent->_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){//旋转处理}else {assert(false);}}

3.1 AVL左单旋

上面图中的h可以认为是抽象图,就是二叉树搜索树的分支。

上面的图可以认为在subR的右子树(记住是右子树)中任意插入一个节点,使右子树的高度+1,导致subR的平衡因子发生改变,从而影响parent的平衡因子变成了2,从而发生旋转。发生左旋转的条件是什么?是parent的平衡因子为2,subR的平衡因子为1时发生左旋转。 

代码实现一下

	void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else {if (parentParent->_left == parent){parentParent->_left = subR;}else {parentParent->_right = subR;}subR->_parent = parentParent;}parent->_bf = subR->_bf = 0;}

3.2 AVL右单旋

subL为parent的左子树,subLR为subL的右子树,当a这个位置高度变为h+1时,parent平衡因子为-2,subL平衡因子为-1时,此时发生右旋转,parent的左子树变为subLR,subL变为父节点且右子树为parent

代码实现一下

	void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}parent->_bf = subL->_bf = 0;}

3. 2 左右双旋

左旋 

如果subRL这个二叉树中高度增加1时也就是说当parent的平衡因子为2,subR的平衡因子为-1时左单旋,我们不难发现旋转的结果与预期不符 

右旋如果subLR这个二叉树高度增加1时也就是说parent的平衡因子为-2,subL的平衡因子为1时进行右单旋,旋转的结果与预期不符。旋来旋去发现高度没有变化。

如何解决parent的平衡因子为2,subR的平衡因子为-1的问题?

上图就是解决方式,增加节点后的第二个图,90为parent进行右旋到第三张图,之后30再变成parent进行左旋。

这里最麻烦的是平衡因子的维护

当h == 0时

也就是说subRL这个节点为新增节点,subRL与parent与subR平衡因子都为0。

当h > 0 时 

因为增加节点有可能在b或者在c,不能确定parent这个位置平衡因子是-1/0还是subR这个位置的平衡因子是0/1,如何解决呢?

我们记录一下插入节点后subRL的平衡因子,如果subRL的平衡因子为1,经过左右双旋后,parent平衡因子为-1,subR为0,subRL为0。如果subRL的平衡因子为-1,经过左右双旋后,parent的平衡因子为0,subR为1,subRL为0。

代码实现

	void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){subRL->_bf = 0;subR->_bf = 0;parent->_bf = 0;}else if (bf == 1){subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}}

如何解决parent的平衡因子为-2,subR的平衡因子为1的问题?

 上图就是解决方式,增加节点后的第二个图,30为parent进行左旋到第三张图,之后90再变成parent进行右旋。

 平衡因子的维护

 这里也要分三种情况来进行判断

当h == 0时

subRL为新插入节点时,parent的平衡因子与subL和subLR都为0

当h > 0时

新增节点插入b位置时,subLR的平衡因子为-1时,双旋后parent的平衡因子为1,subL和subLR的平衡因子为0。

新增节点插入c位置时,subLR的平衡因子为1时,双旋后parent的平衡因子为0,subL的平衡因子为-1,subLR的平衡因子为0。

 代码实现

	void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else if (bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else {assert(false);}}


http://www.mrgr.cn/news/39345.html

相关文章:

  • P10483 小猫爬山
  • 深度估计任务中的有监督和无监督训练
  • 微信功能限制带来的创业机会案例分享
  • vue admin 若依框架 解决无权限时进入死循环的问题 auths
  • C++的6种构造函数
  • 前缀和(6)_和可被k整除的子数组_蓝桥杯
  • cloud-(Nacos)--注册中心原理-服务注册-服务发现
  • HTB:Three[WriteUP]
  • 如何使用 git 克隆特定 tag 的代码 ?
  • 招联金融内推-2025校招
  • 每日1题-7
  • 自动化测试常见的面试题(超详细整理)
  • 什么是敏捷迭代开发模型
  • 组合逻辑元件与时序逻辑元件
  • 9.29总结
  • 1688客服代码怎么做生成悬浮客服代码阿里巴巴国内站1688平台悬浮特效悬浮代码悬浮客服 1688客服代码怎么做生成器软件代码工具制作客服代码阿里巴巴
  • 【muduo源码分析】「阻塞」「非阻塞」「同步」「异步」
  • 专深与广博的平衡艺术
  • 9.29学习
  • 兼容React的刮刮乐完整代码实现